buefy
Version:
Lightweight UI components for Vue.js (v3) based on Bulma
305 lines (250 loc) • 9.78 kB
text/typescript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { shallowMount } from '@vue/test-utils'
import type { VueWrapper } from '@vue/test-utils'
import { defineComponent } from 'vue'
import BCarousel from '@components/carousel/Carousel.vue'
import InjectedChildMixin, { Sorted } from '../../utils/InjectedChildMixin'
type BCarouselInstance = InstanceType<typeof BCarousel>
let wrapper: VueWrapper<BCarouselInstance>
const mockCarouselItems = defineComponent({
name: 'BCarouselItem',
mixins: [InjectedChildMixin<typeof Sorted, BCarouselInstance>('carousel', Sorted)],
computed: {
isActive() {
return this.parent.activeChild === this.index
}
},
template: '<div></div>'
})
describe('BCarousel', () => {
beforeEach(() => {
wrapper = shallowMount(BCarousel, {
props: {
autoplay: false,
repeat: false
},
slots: {
default: [
'<b-carousel-item/>',
'<b-carousel-item/>'
]
},
global: {
stubs: { 'b-carousel-item': mockCarouselItems }
}
})
})
afterEach(() => {
vi.useRealTimers()
})
it('is called', () => {
expect(wrapper.vm).toBeTruthy()
expect(wrapper.vm.$options.name).toBe('BCarousel')
})
it('render correctly', () => {
expect(wrapper.html()).toMatchSnapshot()
})
it('reacts when value changes', async () => {
let value = 1
await wrapper.setProps({ modelValue: value })
expect(wrapper.vm.activeChild).toBe(value)
value = 3
await wrapper.setProps({ modelValue: value })
expect(wrapper.vm.activeChild).toBe(1)
value = 0
await wrapper.setProps({ modelValue: value })
expect(wrapper.vm.activeChild).toBe(value)
})
it('reacts when autoplay changes', async () => {
wrapper.vm.startTimer = vi.fn(wrapper.vm.startTimer)
wrapper.vm.pauseTimer = vi.fn(wrapper.vm.pauseTimer)
wrapper.vm.next = vi.fn(wrapper.vm.next)
let autoplay = true
await wrapper.setProps({ autoplay })
expect(wrapper.vm.autoplay).toBe(autoplay)
expect(wrapper.vm.startTimer).toHaveBeenCalled()
autoplay = false
await wrapper.setProps({ autoplay })
expect(wrapper.vm.autoplay).toBe(autoplay)
expect(wrapper.vm.pauseTimer).toHaveBeenCalled()
})
it('returns item classes accordingly', async () => {
const indicatorBackground = true
const indicatorCustom = true
const indicatorInside = true
const indicatorCustomSize = 'is-small'
const indicatorPosition = 'is-bottom'
await wrapper.setProps({
indicatorBackground,
indicatorCustom,
indicatorInside,
indicatorCustomSize,
indicatorPosition
})
expect(wrapper.vm.indicatorClasses).toEqual([
{
'has-background': indicatorBackground,
'has-custom': indicatorCustom,
'is-inside': indicatorInside
},
indicatorCustom && indicatorCustomSize,
indicatorInside && indicatorPosition
])
})
it('manage next and previous accordingly', async () => {
wrapper.vm.startTimer = vi.fn(() => wrapper.vm.startTimer)
wrapper.vm.pauseTimer = vi.fn(() => wrapper.vm.pauseTimer)
const first = 0
const last = 1
let repeat = false
await wrapper.setProps({ modelValue: last, repeat })
wrapper.vm.prev()
expect(wrapper.vm.activeChild).toBe(first)
wrapper.vm.prev()
expect(wrapper.vm.activeChild).toBe(first) // Wont go below 0 without repeat prop
repeat = true
await wrapper.setProps({ repeat })
wrapper.vm.prev()
expect(wrapper.vm.activeChild).toBe(last) // Will be set to the last value using repeat
expect(wrapper.vm.startTimer).toHaveBeenCalled()
wrapper.vm.next()
expect(wrapper.vm.activeChild).toBe(first) // Navigate to the first value with repeat
wrapper.vm.next()
expect(wrapper.vm.activeChild).toBe(last)
repeat = false
await wrapper.setProps({ repeat })
wrapper.vm.next()
expect(wrapper.vm.activeChild).toBe(last) // Wont go above last when not using repeat
})
it('manage interaction with indicators', async () => {
const indicator = wrapper.find('.indicator-item')
const first = 0
const last = 1
await wrapper.setProps({ modelValue: last })
await indicator.trigger('mouseover') // no change since indicatorMode is 'click'
expect(wrapper.vm.activeChild).toBe(last)
await indicator.trigger('click')
expect(wrapper.vm.activeChild).toBe(first)
})
it('autoplays', async () => {
vi.useFakeTimers()
await wrapper.setProps({ autoplay: true, pauseHover: false, repeat: false })
expect(wrapper.vm.activeChild).toBe(0)
vi.runOnlyPendingTimers()
await wrapper.vm.$nextTick()
expect(wrapper.vm.activeChild).toBe(1)
vi.runOnlyPendingTimers()
await wrapper.vm.$nextTick()
expect(wrapper.vm.activeChild).toBe(1)
await wrapper.setProps({ repeat: true })
await wrapper.vm.$nextTick()
vi.runOnlyPendingTimers()
await wrapper.vm.$nextTick()
expect(wrapper.vm.activeChild).toBe(0)
})
it('pauses on hover', async () => {
vi.useFakeTimers()
await wrapper.setProps({ autoplay: true, pauseHover: true, repeat: true })
vi.runOnlyPendingTimers()
await wrapper.vm.$nextTick()
expect(wrapper.vm.activeChild).toBe(1)
vi.runOnlyPendingTimers()
await wrapper.vm.$nextTick()
expect(wrapper.vm.activeChild).toBe(0)
await wrapper.find('.carousel').trigger('mouseenter')
vi.runOnlyPendingTimers()
await wrapper.vm.$nextTick()
expect(wrapper.vm.activeChild).toBe(0)
await wrapper.find('.carousel').trigger('mouseleave')
expect(wrapper.vm.activeChild).toBe(0)
vi.runOnlyPendingTimers()
await wrapper.vm.$nextTick()
expect(wrapper.vm.activeChild).toBe(1)
})
it('drags correctly on mobile', async () => {
const first = 0
const last = 1
await wrapper.setProps({ modelValue: first })
wrapper.vm.startTimer = vi.fn(() => wrapper.vm.startTimer)
wrapper.vm.pauseTimer = vi.fn(() => wrapper.vm.pauseTimer)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const event: any = {
target: {
draggable: true
},
touches: true,
changedTouches: [{
pageX: 50
}]
} // predents to be a TouchEvent
// Dragging enough to go to next slide
await wrapper.vm.$nextTick()
wrapper.vm.dragStart(event)
expect(wrapper.vm.pauseTimer).toHaveBeenCalled()
await wrapper.vm.$nextTick()
event.changedTouches[0].pageX = 0
wrapper.vm.dragEnd(event)
expect(wrapper.vm.activeChild).toBe(last)
// Dragging enough to go to previous slide
await wrapper.vm.$nextTick()
wrapper.vm.dragStart(event)
expect(wrapper.vm.pauseTimer).toHaveBeenCalled()
await wrapper.vm.$nextTick()
event.changedTouches[0].pageX = 50
wrapper.vm.dragEnd(event)
expect(wrapper.vm.activeChild).toBe(first)
expect(wrapper.vm.startTimer).toHaveBeenCalled()
})
it('drags correctly on desktop', async () => {
const first = 0
const last = 1
await wrapper.setProps({ modelValue: first })
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const event: any = {
target: {
draggable: true,
click: vi.fn()
},
pageX: 50,
preventDefault: vi.fn()
} // pretends to be a MouseEvent
// Dragging enough to go to next slide
await wrapper.vm.$nextTick()
wrapper.vm.dragStart(event)
expect(event.preventDefault).toHaveBeenCalled()
await wrapper.vm.$nextTick()
event.pageX = 0
wrapper.vm.dragEnd(event)
expect(wrapper.vm.activeChild).toBe(last)
// Dragging enough to go to previous slide
await wrapper.vm.$nextTick()
wrapper.vm.dragStart(event)
expect(event.preventDefault).toHaveBeenCalled()
await wrapper.vm.$nextTick()
event.pageX = 50
wrapper.vm.dragEnd(event)
expect(wrapper.vm.activeChild).toBe(first)
// Considering a tiny slide for a click
await wrapper.vm.$nextTick()
wrapper.vm.dragStart(event)
expect(event.preventDefault).toHaveBeenCalled()
await wrapper.vm.$nextTick()
event.pageX = 55
wrapper.vm.dragEnd(event)
expect(wrapper.vm.activeChild).toBe(first)
expect(event.target.click).toHaveBeenCalled()
expect(wrapper.emitted().click).toBeTruthy()
})
it('destroys correctly', async () => {
await wrapper.setProps({ autoplay: true })
await wrapper.vm.$nextTick()
expect(wrapper.vm.timer).toBeTruthy()
wrapper.unmount()
expect(wrapper.vm.timer).toBeFalsy()
})
it('reset timer before destroy', () => {
wrapper.vm.pauseTimer = vi.fn(() => wrapper.vm.pauseTimer)
wrapper.unmount()
expect(wrapper.vm.pauseTimer).toHaveBeenCalled()
})
})