add test for ValidatedInput

This commit is contained in:
einhornimmond 2025-07-25 13:32:46 +02:00
parent f1052409c0
commit 91fa52b7b6
2 changed files with 95 additions and 3 deletions

View File

@ -0,0 +1,92 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import ValidatedInput from '@/components/Inputs/ValidatedInput.vue'
import * as yup from 'yup'
import { BFormInvalidFeedback, BFormInput, BFormTextarea, BFormGroup } from 'bootstrap-vue-next'
import LabeledInput from '@/components/Inputs/LabeledInput.vue'
vi.mock('vue-i18n', () => ({
useI18n: () => ({
t: (key) => key,
n: (n) => String(n),
}),
}))
describe('ValidatedInput', () => {
let wrapper
const createWrapper = (props = {}) =>
mount(ValidatedInput, {
props: {
label: 'Test Label',
modelValue: '',
name: 'testInput',
rules: yup.string().required().min(3).default(''),
...props,
},
global: {
mocks: {
$t: (key) => key,
$i18n: {
locale: 'en',
},
$n: (n) => String(n),
},
components: {
BFormInvalidFeedback,
BFormInput,
BFormTextarea,
BFormGroup,
LabeledInput,
},
},
})
beforeEach(() => {
wrapper = createWrapper()
})
it('renders the label and input', () => {
expect(wrapper.text()).toContain('Test Label')
const input = wrapper.find('input')
expect(input.exists()).toBe(true)
})
it('starts with neutral validation state', () => {
const input = wrapper.find('input')
expect(input.classes()).not.toContain('is-valid')
expect(input.classes()).not.toContain('is-invalid')
})
it('shows green border when value is valid before blur', async () => {
await wrapper.setProps({ modelValue: 'validInput' })
await wrapper.vm.$nextTick()
const input = wrapper.find('input')
expect(input.classes()).toContain('is-valid')
expect(input.classes()).not.toContain('is-invalid')
})
it('does not show red border before blur even if invalid', async () => {
await wrapper.setProps({ modelValue: 'a' })
const input = wrapper.find('input')
expect(input.classes()).not.toContain('is-invalid')
})
it('shows red border and error message after blur when input is invalid', async () => {
await wrapper.setProps({ modelValue: 'a' })
const input = wrapper.find('input')
await input.trigger('blur')
await wrapper.vm.$nextTick()
expect(input.classes()).toContain('is-invalid')
expect(wrapper.text()).toContain('this must be at least 3 characters')
})
it('emits update:modelValue on input', async () => {
const input = wrapper.find('input')
await input.setValue('hello')
await wrapper.vm.$nextTick()
expect(wrapper.emitted()['update:modelValue']).toBeTruthy()
const [value, name] = wrapper.emitted()['update:modelValue'][0]
expect(value).toBe('hello')
expect(name).toBe('testInput')
})
})

View File

@ -48,7 +48,7 @@ const model = ref(props.modelValue !== 0 ? props.modelValue : '')
// prevent showing errors on form init
const afterFirstInput = ref(false)
const valid = computed(() => props.rules.isValidSync(props.modelValue))
const valid = computed(() => props.rules.isValidSync(model.value))
// smartValidState controls the visual validation feedback for the input field.
// The goal is to avoid showing red (invalid) borders too early, creating a smoother UX:
//
@ -67,11 +67,11 @@ const smartValidState = computed(() => {
return valid.value ? true : null
})
const errorMessage = computed(() => {
if (props.modelValue === undefined || props.modelValue === '' || props.modelValue === null) {
if (model.value === undefined || model.value === '' || model.value === null) {
return undefined
}
try {
props.rules.validateSync(props.modelValue)
props.rules.validateSync(model.value)
return undefined
} catch (e) {
return translateYupErrorString(e.message, t)