Files
malio-layer-ui/app/components/malio/input/InputAmount.test.ts
tristan f3a18ace1d
All checks were successful
Release / release (push) Successful in 1m12s
feat: composant saisie assistée, composant téléphone et composant mail (#47)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

- [ ] Pas de régression
- [ ] TU/TI/TF rédigée
- [ ] TU/TI/TF OK
- [ ] CHANGELOG modifié

Co-authored-by: matthieu <matthieu@yuno.malio.fr>
Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
Reviewed-on: #47
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-05-13 07:01:30 +00:00

178 lines
5.5 KiB
TypeScript

import {describe, expect, it} from 'vitest'
import {mount} from '@vue/test-utils'
import type {DefineComponent} from 'vue'
import InputAmount from './InputAmount.vue'
type InputAmountProps = {
id?: string
label?: string
name?: string
autocomplete?: string
modelValue?: string | null
inputClass?: string
labelClass?: string
groupClass?: string
required?: boolean
maxLength?: number | string
minLength?: number | string
disabled?: boolean
readonly?: boolean
hint?: string
error?: string
success?: string
iconName?: string
iconPosition?: 'left' | 'right'
iconSize?: string | number
iconColor?: string
}
const InputAmountForTest = InputAmount as DefineComponent<InputAmountProps>
const mountInputAmount = (props: InputAmountProps = {}) =>
mount(InputAmountForTest, {
props,
global: {
stubs: {
IconifyIcon: {
template: '<span data-test="icon" v-bind="$attrs" />',
},
},
},
})
describe('MalioInputAmount', () => {
it('renders as a text input with decimal input mode', () => {
const wrapper = mountInputAmount()
expect(wrapper.get('input').attributes('type')).toBe('text')
expect(wrapper.get('input').attributes('inputmode')).toBe('decimal')
})
it('renders the default icon with muted styling', () => {
const wrapper = mountInputAmount()
expect(wrapper.get('[data-test="icon"]').exists()).toBe(true)
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-m-muted')
expect(wrapper.get('[data-test="icon"]').classes()).toContain('right-[10px]')
})
it('generates an amount-specific id', () => {
const wrapper = mountInputAmount({label: 'Montant'})
const inputId = wrapper.get('input').attributes('id')
expect(inputId?.startsWith('malio-input-amount-')).toBe(true)
expect(wrapper.get('label').attributes('for')).toBe(inputId)
})
it('applies the provided input classes', () => {
const wrapper = mountInputAmount({inputClass: 'text-right'})
expect(wrapper.get('input').classes()).toContain('text-right')
})
it('links hint text through aria-describedby', () => {
const wrapper = mountInputAmount({hint: 'Saisissez un montant'})
const inputId = wrapper.get('input').attributes('id')
expect(wrapper.get('input').attributes('aria-describedby')).toBe(`${inputId}-describedby`)
expect(wrapper.get('p').attributes('id')).toBe(`${inputId}-describedby`)
})
it('sets aria-invalid and describedby when showing an error', () => {
const wrapper = mountInputAmount({error: 'Montant invalide'})
const inputId = wrapper.get('input').attributes('id')
expect(wrapper.get('input').attributes('aria-invalid')).toBe('true')
expect(wrapper.get('input').attributes('aria-describedby')).toBe(`${inputId}-describedby`)
expect(wrapper.get('p.text-m-danger').text()).toBe('Montant invalide')
})
it('keeps dots as the decimal separator on input', async () => {
const wrapper = mountInputAmount({modelValue: ''})
await wrapper.get('input').setValue('12.5')
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['12.5'])
expect(wrapper.get('input').element.value).toBe('12.5')
})
it('accepts commas but normalizes them to dots', async () => {
const wrapper = mountInputAmount({modelValue: ''})
await wrapper.get('input').setValue('0012,345abc')
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['12.34'])
expect(wrapper.get('input').element.value).toBe('12.34')
})
it('normalizes a leading decimal separator', async () => {
const wrapper = mountInputAmount({modelValue: ''})
await wrapper.get('input').setValue(',5')
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['0.5'])
expect(wrapper.get('input').element.value).toBe('0.5')
})
it('keeps the normalized decimal value on blur', async () => {
const wrapper = mountInputAmount()
const input = wrapper.get('input')
await input.setValue('12.5')
await input.trigger('blur')
expect(wrapper.emitted('update:modelValue')).toEqual([['12.5']])
expect(input.element.value).toBe('12.5')
})
it('keeps integer values unchanged on blur', async () => {
const wrapper = mountInputAmount()
const input = wrapper.get('input')
await input.setValue('12')
await input.trigger('blur')
expect(wrapper.emitted('update:modelValue')).toEqual([['12']])
expect(input.element.value).toBe('12')
})
it('keeps an empty value empty on blur', async () => {
const wrapper = mountInputAmount()
const input = wrapper.get('input')
await input.setValue('')
await input.trigger('blur')
expect(wrapper.emitted('update:modelValue')).toEqual([['']])
expect(input.element.value).toBe('')
})
it('supports icon positioning on the left', () => {
const wrapper = mountInputAmount({
label: 'Montant',
iconPosition: 'left',
})
expect(wrapper.get('[data-test="icon"]').classes()).toContain('left-[10px]')
expect(wrapper.get('input').classes()).toContain('!pl-11')
expect(wrapper.get('label').classes()).toContain('left-11')
})
it('shows primary icon color on focus', async () => {
const wrapper = mountInputAmount()
await wrapper.get('input').trigger('focus')
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-m-primary')
})
it('shows black icon color when filled and unfocused', () => {
const wrapper = mountInputAmount({modelValue: '12,50'})
expect(wrapper.get('[data-test="icon"]').classes()).toContain('text-black')
})
})