buefy
Version:
Lightweight UI components for Vue.js (v3) based on Bulma
446 lines (365 loc) • 17.1 kB
text/typescript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { shallowMount, mount } from '@vue/test-utils'
import type { VueWrapper } from '@vue/test-utils'
import BNumberinput from '@components/numberinput/Numberinput.vue'
let wrapper: VueWrapper<InstanceType<typeof BNumberinput>>
describe('BNumberinput', () => {
describe('Rendered', () => {
beforeEach(() => {
wrapper = mount(BNumberinput)
})
afterEach(() => {
vi.useRealTimers()
})
it('is called', () => {
expect(wrapper.vm).toBeTruthy()
expect(wrapper.vm.$options.name).toBe('BNumberinput')
})
it('renders by default', () => {
expect(wrapper.find('input').exists()).toBeTruthy()
expect(wrapper.find('button').exists()).toBeTruthy()
expect(wrapper.classes()).toContain('is-grouped')
})
it('set controls position', async () => {
await wrapper.setProps({
controlsPosition: 'compact'
})
expect(wrapper.find('input').exists()).toBeTruthy()
expect(wrapper.find('button').exists()).toBeTruthy()
expect(wrapper.classes()).toContain('has-addons')
})
it('increments/decrements on long pressing exponentially', async () => {
// we should not depend on a real timer
// otherwise the results will depend on the machine
vi.useFakeTimers()
await wrapper.setProps({ exponential: true, modelValue: 1, step: 1 })
wrapper.find('.control.plus button').trigger('mousedown')
expect(wrapper.vm.computedValue).toBe(2)
for (let n = 1; n <= 10; ++n) {
// while Jest's fake setTimeout truncates the delay to an exact
// ms when it schedules the callback,
// vi.advanceTimersByTime floors the value
// and accumulates the remainder.
// so should be floored to prevent the accumulated remainder
// from triggering an extra callback
vi.advanceTimersByTime(Math.floor(250 / n))
expect(wrapper.vm.computedValue).toBe(n + 2)
}
wrapper.find('.control.plus').trigger('mouseup')
vi.runAllTimers()
await wrapper.vm.$nextTick()
expect(wrapper.vm.computedValue).toBe(12)
await wrapper.setProps({ exponential: 3, modelValue: 0, step: 1 })
wrapper.find('.control.plus button').trigger('mousedown')
expect(wrapper.vm.computedValue).toBe(1)
for (let n = 1; n <= 20; ++n) {
// while Jest's fake setTimeout truncates the delay to an exact
// ms when it schedules the callback,
// vi.advanceTimersByTime floors the value
// and accumulates the remainder.
// so should be floored to prevent the accumulated remainder
// from triggering an extra callback
vi.advanceTimersByTime(Math.floor(250 / (n * 3)))
expect(wrapper.vm.computedValue).toBe(n + 1)
}
wrapper.find('.control.plus').trigger('mouseup')
vi.runAllTimers()
await wrapper.vm.$nextTick()
expect(wrapper.vm.computedValue).toBe(21)
})
it('increments/decrements on long pressing', async () => {
vi.useFakeTimers()
let val = 0
// Increment
wrapper.find('.control.plus button').trigger('mousedown')
val++
// await wait(2000)
vi.runOnlyPendingTimers()
vi.runOnlyPendingTimers()
vi.runOnlyPendingTimers()
val += 3
wrapper.find('.control.plus').trigger('mouseup')
expect(wrapper.vm.computedValue).toBe(val)
// Decrement
wrapper.find('.control.minus button').trigger('mousedown')
val--
vi.runOnlyPendingTimers()
vi.runOnlyPendingTimers()
val -= 2
wrapper.find('.control.minus button').trigger('mouseup')
// Trigger it another time to check for unexpected browser behavior
wrapper.find('.control.minus button').trigger('mouseup')
expect(wrapper.vm.computedValue).toBe(val)
})
it('does not increment/decrement on long pressing when feature is disabled', async () => {
await wrapper.setProps({
longPress: false
})
vi.useFakeTimers()
wrapper.vm.computedValue = 0
// Increment
wrapper.find('.control.plus button').trigger('mousedown')
// await wait(2000)
vi.runOnlyPendingTimers()
vi.runOnlyPendingTimers()
vi.runOnlyPendingTimers()
wrapper.find('.control.plus').trigger('mouseup')
expect(wrapper.vm.computedValue).toBe(1)
// Decrement
wrapper.find('.control.minus button').trigger('mousedown')
vi.runOnlyPendingTimers()
vi.runOnlyPendingTimers()
wrapper.find('.control.minus button').trigger('mouseup')
// Trigger it another time to check for unexpected browser behavior
wrapper.find('.control.minus button').trigger('mouseup')
expect(wrapper.vm.computedValue).toBe(0)
})
it('increments/decrements after manually set value on input', async () => {
await wrapper.setProps({ exponential: true, modelValue: 1, step: 1 })
const BASE_VALUE = Math.floor(Math.random() * 10 + 1)
wrapper.find('input').setValue(BASE_VALUE)
wrapper.find('.control.plus button').trigger('click')
wrapper.find('.control.minus button').trigger('click')
expect(wrapper.vm.computedValue).toEqual(BASE_VALUE)
})
it('is invalid when step / minStep decimals and value decimals lengths are different', async () => {
await wrapper.setProps({ step: 1, modelValue: 1.15 })
expect(wrapper.find('input').element.checkValidity()).toEqual(false)
await wrapper.setProps({ step: 1.15, modelValue: 1.154 })
expect(wrapper.find('input').element.checkValidity()).toEqual(false)
await wrapper.setProps({ step: 1.15, modelValue: 1.11541, minStep: 0.0001 })
expect(wrapper.find('input').element.checkValidity()).toEqual(false)
})
it('is valid when step is "any"', async () => {
await wrapper.setProps({ step: 'any', modelValue: 1.15 })
expect(wrapper.find('input').element.checkValidity()).toEqual(true)
await wrapper.setProps({ step: 'any', modelValue: 1.054878 })
expect(wrapper.find('input').element.checkValidity()).toEqual(true)
await wrapper.setProps({ step: 'any', modelValue: 1 })
expect(wrapper.find('input').element.checkValidity()).toEqual(true)
await wrapper.setProps({ step: 'any', modelValue: null })
expect(wrapper.find('input').element.checkValidity()).toEqual(true)
})
})
describe('Rendered (shallow)', () => {
beforeEach(() => {
wrapper = shallowMount(BNumberinput, {
global: {
stubs: {
'b-input': {
template: '<div></div>',
emits: ['focus', 'blur'], // suppresses warning
methods: {
checkHtml5Validity: () => true
}
}
}
}
})
})
it('manage prop types (number / string)', async () => {
const min = 5
const max = 15
const step = 5
const stepDec = 1.5
const minStep = 0.05
await wrapper.setProps({ min })
expect(wrapper.vm.minNumber).toBe(min)
await wrapper.setProps({ min: `${min}` })
expect(wrapper.vm.minNumber).toBe(min)
await wrapper.setProps({ max })
expect(wrapper.vm.maxNumber).toBe(max)
await wrapper.setProps({ max: `${max}` })
expect(wrapper.vm.maxNumber).toBe(max)
await wrapper.setData({ newStep: step })
expect(wrapper.vm.stepNumber).toBe(step)
expect(wrapper.vm.minStepNumber).toBe(step)
await wrapper.setData({ newStep: `${step}` })
expect(wrapper.vm.stepNumber).toBe(step)
expect(wrapper.vm.minStepNumber).toBe(step)
await wrapper.setData({ newStep: step })
expect(wrapper.vm.stepDecimals).toBe(0)
await wrapper.setData({ newStep: stepDec })
expect(wrapper.vm.stepDecimals).toBe(1)
await wrapper.setData({
newStep: step,
newMinStep: minStep
})
expect(wrapper.vm.stepDecimals).toBe(2)
expect(wrapper.vm.minStepNumber).toBe(minStep)
await wrapper.setData({ newStep: stepDec })
expect(wrapper.vm.stepDecimals).toBe(2)
expect(wrapper.vm.minStepNumber).toBe(minStep)
})
it('manage prop value', async () => {
const value = 5
await wrapper.setProps({ modelValue: value })
expect(wrapper.vm.newValue).toBe(value)
})
it('allows a placeholder', async () => {
const placeholder = 90
const value = 10
await wrapper.setProps({ placeholder })
expect(wrapper.vm.placeholder).toBe(placeholder)
// Only user input should set value, i.e. placeholder shouldn't set value
await wrapper.setProps({ modelValue: value })
expect(wrapper.vm.newValue).toBe(value)
})
it('expects placeholder to not override v-model value', async () => {
const placeholder = 90
const value = 10
const newValue = 20
await wrapper.setProps({ placeholder, modelValue: value })
expect(wrapper.vm.placeholder).toBe(placeholder)
await wrapper.setProps({ modelValue: newValue })
// Only user input should set value, i.e. placeholder shouldn't set value
expect(wrapper.vm.newValue).toBe(newValue)
})
it('allows a string placeholder value', async () => {
const placeholder = '90'
const newValue = 20
await wrapper.setProps({ placeholder })
expect(wrapper.vm.placeholder).toBe(placeholder)
await wrapper.setProps({ modelValue: newValue })
expect(wrapper.vm.modelValue).toBe(20)
expect(wrapper.vm.computedValue).toBe(20)
wrapper.vm.increment()
expect(wrapper.vm.computedValue).toBe(21)
})
it('can increment / decrement', async () => {
const min = 5
const max = 6
wrapper.vm.computedValue = max
await wrapper.setProps({ min })
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(min)
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(min)
await wrapper.setProps({ max })
wrapper.vm.increment()
expect(wrapper.vm.computedValue).toBe(max)
wrapper.vm.increment()
expect(wrapper.vm.computedValue).toBe(max)
})
it('can increment / decrement with a step', async () => {
const start = 5
const step = 3.5
const min = -5
wrapper.vm.computedValue = start
await wrapper.setProps({ step, min })
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(start - step)
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(start - (step * 2))
})
it('can increment / decrement with a "any" step', async () => {
const start = 5
const step = 'any'
const min = -5
wrapper.vm.computedValue = start
await wrapper.setProps({ step, min })
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(start - 1)
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(start - (1 * 2))
const decimalStart = 5.15
wrapper.vm.computedValue = decimalStart
await wrapper.setProps({ step, min })
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(start - 1)
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(start - (1 * 2))
})
it('can increment / decrement with minStep', async () => {
const start = 5.51
const step = 0.2
const minStep = 0.01
const min = -5
wrapper.vm.computedValue = start
await wrapper.setProps({ step, min, minStep })
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(5.31)
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(5.11)
wrapper.vm.increment()
expect(wrapper.vm.computedValue).toBe(5.31)
wrapper.vm.increment()
expect(wrapper.vm.computedValue).toBe(5.51)
const newMinStep = 0.1
await wrapper.setProps({ minStep: newMinStep })
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(5.3)
})
it('can increment / decrement with minStep and "any" as step', async () => {
const start = 5.51
const step = 'any'
const minStep = 0.01
const min = -5
wrapper.vm.computedValue = start
await wrapper.setProps({ step, min, minStep })
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(4.51)
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(3.51)
wrapper.vm.increment()
expect(wrapper.vm.computedValue).toBe(4.51)
wrapper.vm.increment()
expect(wrapper.vm.computedValue).toBe(5.51)
const newMinStep = 0.1
await wrapper.setProps({ minStep: newMinStep })
wrapper.vm.decrement()
expect(wrapper.vm.computedValue).toBe(4.5)
})
it('manages empty value', async () => {
wrapper.vm.computedValue = ''
expect(wrapper.vm.computedValue).toBeNull()
const min = 5
await wrapper.setProps({ min })
wrapper.vm.computedValue = ''
expect(wrapper.vm.computedValue).toBeNull()
})
it('increments/decrements on click', async () => {
await wrapper.setProps({ modelValue: 5, step: 1 })
wrapper.find('.control.plus button').trigger('click')
await wrapper.vm.$nextTick()
expect(wrapper.vm.computedValue).toBe(6)
wrapper.find('.control.minus button').trigger('click')
await wrapper.vm.$nextTick()
expect(wrapper.vm.computedValue).toBe(5)
})
})
describe('with fallthrough attributes', () => {
const attrs = {
class: 'fallthrough-class',
style: 'font-size: 2rem;',
id: 'fallthrough-id'
}
it('should apply class, style, and id to the root <div> element if compatFallthrough is true (default)', () => {
const wrapper = shallowMount(BNumberinput, { attrs })
const root = wrapper.find('div.b-numberinput')
expect(root.classes(attrs.class)).toBe(true)
expect(root.attributes('style')).toBe(attrs.style)
expect(root.attributes('id')).toBe(attrs.id)
const input = wrapper.findComponent({ ref: 'input' })
expect(input.classes(attrs.class)).toBe(false)
expect(input.attributes('style')).toBeUndefined()
expect(input.attributes('id')).toBeUndefined()
})
it('should apply class, style and id to the underlying <b-input> if compatFallthrough is false', () => {
const wrapper = shallowMount(BNumberinput, {
attrs,
props: {
compatFallthrough: false
}
})
const root = wrapper.find('div.b-numberinput')
expect(root.classes(attrs.class)).toBe(false)
expect(root.attributes('style')).toBeUndefined()
expect(root.attributes('id')).toBeUndefined()
const input = wrapper.findComponent({ ref: 'input' })
expect(input.classes(attrs.class)).toBe(true)
expect(input.attributes('style')).toBe(attrs.style)
expect(input.attributes('id')).toBe(attrs.id)
})
})
})