From 1f25e0d8632e50b7c2059fc5ad39d45918c28271 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 2 Feb 2025 15:53:04 +0800 Subject: [PATCH] test(vapor): v-model runtime tests --- packages/runtime-dom/src/directives/vModel.ts | 11 +- .../__tests__/directives/vModel.spec.ts | 748 ++++++++---------- packages/runtime-vapor/src/dom/prop.ts | 10 + 3 files changed, 364 insertions(+), 405 deletions(-) diff --git a/packages/runtime-dom/src/directives/vModel.ts b/packages/runtime-dom/src/directives/vModel.ts index 256b91559..ee6cfcbab 100644 --- a/packages/runtime-dom/src/directives/vModel.ts +++ b/packages/runtime-dom/src/directives/vModel.ts @@ -90,7 +90,7 @@ export const vModelTextInit = ( if (trim) { domValue = domValue.trim() } - if (number) { + if (number || el.type === 'number') { domValue = looseToNumber(domValue) } ;(set || (el as any)[assignKey])(domValue) @@ -359,7 +359,14 @@ function getCheckboxValue( checked: boolean, ) { const key = checked ? '_trueValue' : '_falseValue' - return key in el ? el[key] : checked + if (key in el) { + return el[key] + } + const attr = checked ? 'true-value' : 'false-value' + if (el.hasAttribute(attr)) { + return el.getAttribute(attr) + } + return checked } export const vModelDynamic: ObjectDirective< diff --git a/packages/runtime-vapor/__tests__/directives/vModel.spec.ts b/packages/runtime-vapor/__tests__/directives/vModel.spec.ts index 4a744a5f3..5429cc1b3 100644 --- a/packages/runtime-vapor/__tests__/directives/vModel.spec.ts +++ b/packages/runtime-vapor/__tests__/directives/vModel.spec.ts @@ -1,17 +1,16 @@ import { reactive, ref } from '@vue/reactivity' import { + applyCheckboxModel, + applyRadioModel, + applySelectModel, + applyTextModel, delegate, delegateEvents, on, setClass, - setDOMProp, + setProp, + setValue, template, - // @ts-expect-error - vModelDynamic, - // @ts-expect-error - vModelSelect, - // @ts-expect-error - withDirectives, } from '../../src' import { makeRender } from '../_utils' import { nextTick } from '@vue/runtime-dom' @@ -26,11 +25,11 @@ const triggerEvent = (type: string, el: Element) => { const setDOMProps = (el: any, props: Array<[key: string, value: any]>) => { props.forEach(prop => { const [key, value] = prop - key === 'class' ? setClass(el, value) : setDOMProp(el, key, value) + key === 'class' ? setClass(el, value) : setProp(el, key, value) }) } -describe.todo('directive: v-model', () => { +describe('directive: v-model', () => { test('should work with text input', async () => { const spy = vi.fn() @@ -39,8 +38,11 @@ describe.todo('directive: v-model', () => { const t0 = template('') delegateEvents('input') const n0 = t0() as HTMLInputElement - withDirectives(n0, [[vModelDynamic, () => data.value]]) - delegate(n0, 'update:modelValue', () => val => (data.value = val)) + applyTextModel( + n0, + () => data.value, + val => (data.value = val), + ) delegate(n0, 'input', () => () => spy(data.value)) return n0 }).render() @@ -70,9 +72,12 @@ describe.todo('directive: v-model', () => { const t0 = template( '', ) - const n0 = t0() as HTMLInputElement - withDirectives(n0, [[vModelSelect, () => data.value]]) - delegate(n0, 'update:modelValue', () => val => (data.value = val)) + const n0 = t0() as HTMLSelectElement + applySelectModel( + n0, + () => data.value, + val => (data.value = val), + ) on(n0, 'change', () => () => spy(data.value)) return n0 }).render() @@ -96,10 +101,12 @@ describe.todo('directive: v-model', () => { const { host } = define(() => { const t0 = template('') const n0 = t0() as HTMLInputElement - - setDOMProp(n0, 'type', 'number') - withDirectives(n0, [[vModelDynamic, () => data.value]]) - delegate(n0, 'update:modelValue', () => val => (data.value = val)) + applyTextModel( + n0, + () => data.value, + val => (data.value = val), + ) + n0.type = 'number' return n0 }).render() @@ -115,66 +122,16 @@ describe.todo('directive: v-model', () => { expect(data.value).toEqual(1) }) - test('should work with multiple listeners', async () => { - const spy = vi.fn() - - const data = ref('') - const { host } = define(() => { - const t0 = template('') - const n0 = t0() as HTMLInputElement - withDirectives(n0, [[vModelDynamic, () => data.value]]) - delegate(n0, 'update:modelValue', () => val => (data.value = val)) - delegate(n0, 'update:modelValue', () => spy) - return n0 - }).render() - - const input = host.querySelector('input')! - - input.value = 'foo' - triggerEvent('input', input) - await nextTick() - expect(data.value).toEqual('foo') - expect(spy).toHaveBeenCalledWith('foo') - }) - - test('should work with updated listeners', async () => { - const spy1 = vi.fn() - const spy2 = vi.fn() - const toggle = ref(true) - - const data = ref('') - const { host } = define(() => { - const t0 = template('') - const n0 = t0() as HTMLInputElement - withDirectives(n0, [[vModelDynamic, () => data.value]]) - delegate(n0, 'update:modelValue', () => (toggle.value ? spy1 : spy2)) - return n0 - }).render() - - const input = host.querySelector('input')! - input.value = 'foo' - triggerEvent('input', input) - await nextTick() - expect(spy1).toHaveBeenCalledWith('foo') - - // update listener - toggle.value = false - await nextTick() - - input.value = 'bar' - triggerEvent('input', input) - await nextTick() - expect(spy1).not.toHaveBeenCalledWith('bar') - expect(spy2).toHaveBeenCalledWith('bar') - }) - test('should work with textarea', async () => { const data = ref('') const { host } = define(() => { const t0 = template('