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 const mountInputAmount = (props: InputAmountProps = {}) => mount(InputAmountForTest, { props, global: { stubs: { IconifyIcon: { template: '', }, }, }, }) 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-2') }) 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-error').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-2') expect(wrapper.get('input').classes()).toContain('!pl-11') expect(wrapper.get('label').classes()).toContain('left-8') }) })