/* eslint-disable @typescript-eslint/no-explicit-any */
import { config, mount, shallowMount, VueWrapper } from '@vue/test-utils'
import { beforeEach, describe, expect, it } from 'vitest'
import AmeliproTextField from '../AmeliproTextField.vue'
import type { ComponentProps } from 'vue-component-type-helpers'
import type { ExpectedPropOptions } from '@tests/types'
import { defineComponent, type PropType } from 'vue'
import TestHelper from '@tests/helpers/TestHelper'
import type { ValidateOnType } from '../../types'
import type { ValidationRule } from '@/utils/rules/types'
const VTextFieldStub = defineComponent({
name: 'VTextField',
props: {
modelValue: {
type: [String, Number],
default: undefined,
},
placeholder: {
type: String,
default: undefined,
},
readonly: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
clearable: {
type: Boolean,
default: false,
},
id: {
type: String,
default: undefined,
},
required: {
type: [Boolean, String],
default: false,
},
ariaInvalid: {
type: [Boolean, String],
default: false,
},
ariaDescribedby: {
type: String,
default: undefined,
},
hideDetails: {
type: [Boolean, String],
default: false,
},
style: {
type: [String, Object],
default: undefined,
},
type: {
type: String,
default: 'text',
},
max: {
type: String,
default: undefined,
},
min: {
type: String,
default: undefined,
},
counter: {
type: [Boolean, Number, String],
default: undefined,
},
validateOn: {
type: String,
default: undefined,
},
rules: {
type: Array,
default: () => [],
},
},
emits: ['update:modelValue', 'change', 'focus', 'blur'],
template: `
`,
})
config.global.stubs = config.global.stubs || {}
config.global.stubs.VTextField = VTextFieldStub
const expectedPropOptions: ExpectedPropOptions = {
required: {
type: Boolean,
default: false,
},
classes: {
type: String,
default: undefined,
},
clearable: {
type: Boolean,
default: false,
},
counter: {
type: [Boolean, Number, String] as PropType,
default: undefined,
},
disabled: {
type: Boolean,
default: false,
},
disabledDateForSafari: {
type: Boolean,
default: false,
},
fullWidthErrorMsg: {
type: Boolean,
default: false,
},
globalMaxWidth: {
type: String,
default: undefined,
},
globalMinWidth: {
type: String,
default: undefined,
},
globalWidth: {
type: String,
default: undefined,
},
hideErrorMessage: {
type: [String, Boolean] as PropType,
default: false,
},
horizontal: {
type: Boolean,
default: false,
},
inputMaxWidth: {
type: String,
default: undefined,
},
inputMinWidth: {
type: String,
default: undefined,
},
isValidationList: {
type: Boolean,
default: false,
},
label: {
type: String,
required: true,
},
labelMaxWidth: {
type: String,
default: undefined,
},
labelMinWidth: {
type: String,
default: undefined,
},
maxDate: {
type: String,
default: undefined,
},
maxNumber: {
type: String,
default: undefined,
},
minDate: {
type: String,
default: undefined,
},
minNumber: {
type: String,
default: undefined,
},
modelValue: {
type: [String, Number] as PropType,
default: undefined,
},
placeholder: {
type: String,
default: undefined,
},
readonly: {
type: Boolean,
default: false,
},
rules: {
type: Array as PropType,
default: () => [],
},
type: {
type: String,
default: 'text',
},
uniqueId: {
type: String,
required: true,
},
validateOn: {
default: undefined,
type: String as PropType,
validator(value: string): boolean {
return ['lazy', 'blur', 'input', 'submit', 'blur lazy', 'input lazy', 'submit lazy', 'lazy blur', 'lazy input', 'lazy submit'].includes(value.toLowerCase())
},
},
}
const requiredPropValues = (): ComponentProps => ({
label: 'Required label',
uniqueId: 'required-unique-id',
})
const modifiedPropValues = (): ComponentProps => ({
required: true,
classes: 'modified-classes',
clearable: true,
counter: 5,
disabled: true,
disabledDateForSafari: true,
fullWidthErrorMsg: true,
globalMaxWidth: '300px',
globalMinWidth: '100px',
globalWidth: '200px',
hideErrorMessage: 'auto',
horizontal: true,
inputMaxWidth: '150px',
inputMinWidth: '50px',
label: 'Modified label',
labelMaxWidth: '120px',
labelMinWidth: '60px',
maxDate: '2099-12-31',
maxNumber: '100',
minDate: '2000-01-01',
minNumber: '1',
modelValue: 'Modified model value',
placeholder: 'Modified placeholder',
readonly: true,
rules: [() => true],
type: 'number',
uniqueId: 'modified-unique-id',
validateOn: 'input',
})
const testHelper = new TestHelper(AmeliproTextField)
testHelper.setExpectedPropOptions(expectedPropOptions)
.setRequiredPropValues(requiredPropValues)
.setModifiedPropValues(modifiedPropValues)
describe('AmeliproTextField', () => {
describe('Snapshots', () => {
testHelper.snapshots()
})
describe('Properties', () => {
testHelper.properties()
})
describe('Setting props should update attributes of inner tags', () => {
let vueWrapper: VueWrapper
beforeEach(() => {
vueWrapper = shallowMount(AmeliproTextField, { props: requiredPropValues() })
})
it('prop uniqueId sets attribute id on root container', async () => {
const defaultId = testHelper.default('uniqueId')
let container = vueWrapper.find(`#${defaultId}-container`)
expect(container.exists()).toBe(true)
expect(container.attributes('id')).toBe(`${defaultId}-container`)
const { uniqueId } = modifiedPropValues()
await vueWrapper.setProps({ uniqueId })
container = vueWrapper.find(`#${uniqueId}-container`)
expect(container.exists()).toBe(true)
expect(container.attributes('id')).toBe(`${uniqueId}-container`)
})
it('prop label sets label text', async () => {
expect(vueWrapper.find('.amelipro-text-field__label').text()).toContain(testHelper.default('label'))
const { label } = modifiedPropValues()
await vueWrapper.setProps({ label })
expect(vueWrapper.find('.amelipro-text-field__label').text()).toContain(testHelper.modified('label'))
})
})
describe('Setting props should update props or attributes of inner components', () => {
let vueWrapper: VueWrapper
beforeEach(() => {
vueWrapper = shallowMount(AmeliproTextField, { props: requiredPropValues() })
})
it('prop disabled sets prop disabled on VTextField', async () => {
expect(vueWrapper.findComponent({ name: 'VTextField' }).props('disabled')).toBe(testHelper.default('disabled'))
const { disabled } = modifiedPropValues()
await vueWrapper.setProps({ disabled })
expect(vueWrapper.findComponent({ name: 'VTextField' }).props('disabled')).toBe(testHelper.modified('disabled'))
})
it('prop clearable sets prop clearable on VTextField', async () => {
expect(vueWrapper.findComponent({ name: 'VTextField' }).props('clearable')).toBe(testHelper.default('clearable'))
const { clearable } = modifiedPropValues()
await vueWrapper.setProps({ clearable })
expect(vueWrapper.findComponent({ name: 'VTextField' }).props('clearable')).toBe(testHelper.modified('clearable'))
})
it('prop placeholder sets prop placeholder on VTextField', async () => {
expect(vueWrapper.findComponent({ name: 'VTextField' }).props('placeholder')).toBe(testHelper.default('placeholder'))
const { placeholder } = modifiedPropValues()
await vueWrapper.setProps({ placeholder })
expect(vueWrapper.findComponent({ name: 'VTextField' }).props('placeholder')).toBe(testHelper.modified('placeholder'))
})
it('prop disabledDateForSafari & type=date sets prop type on VTextField to text on Safari', () => {
// Simule Safari
const originalUserAgent = window.navigator.userAgent
Object.defineProperty(window.navigator, 'userAgent', {
value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15',
configurable: true,
})
vueWrapper = shallowMount(AmeliproTextField, {
props: {
...requiredPropValues(),
disabledDateForSafari: true,
type: 'date',
},
})
expect(vueWrapper.findComponent({ name: 'VTextField' }).props('type')).toBe('text')
// Nettoyage : restaure l'userAgent
Object.defineProperty(window.navigator, 'userAgent', {
value: originalUserAgent,
configurable: true,
})
})
})
describe('Slots', () => {
it('displays slot content in labelInfo', () => {
const vueWrapper = shallowMount(AmeliproTextField, {
props: requiredPropValues(),
slots: { labelInfo: 'Slot Content
' },
})
expect(vueWrapper.find('#slot-content').text()).toBe('Slot Content')
})
// Slot du composant VTextField, non accessible si on utilise shallowMount
it('displays slot content in append', () => {
const vueWrapper = mount(AmeliproTextField, {
props: requiredPropValues(),
slots: { append: 'Append Content
' },
})
expect(vueWrapper.find('#append-content').text()).toBe('Append Content')
})
})
describe('Events', () => {
it('emits update:model-value on input change', async () => {
const vueWrapper = mount(AmeliproTextField, { props: requiredPropValues() })
await vueWrapper.findComponent({ name: 'VTextField' }).vm.$emit('update:modelValue', 'foo')
await vueWrapper.vm.$nextTick()
expect(vueWrapper.emitted('update:model-value')).toBeTruthy()
})
it('emits change on change event', async () => {
const vueWrapper = mount(AmeliproTextField, { props: requiredPropValues() })
await vueWrapper.findComponent({ name: 'VTextField' }).vm.$emit('change')
await vueWrapper.vm.$nextTick()
expect(vueWrapper.emitted('change')).toBeTruthy()
})
})
})