| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- import { BindingTypes } from '@vue/compiler-core'
- import { assertCode, compileSFCScript as compile } from '../utils'
- describe('defineModel()', () => {
- test('basic usage', () => {
- const { content, bindings } = compile(
- `
- <script setup>
- const modelValue = defineModel({ required: true })
- const c = defineModel('count')
- const toString = defineModel('toString', { type: Function })
- </script>
- `,
- )
- assertCode(content)
- expect(content).toMatch('props: {')
- expect(content).toMatch('"modelValue": { required: true },')
- expect(content).toMatch('"count": {},')
- expect(content).toMatch('"toString": { type: Function },')
- expect(content).toMatch(
- 'emits: ["update:modelValue", "update:count", "update:toString"],',
- )
- expect(content).toMatch(
- `const modelValue = _useModel(__props, "modelValue")`,
- )
- expect(content).toMatch(`const c = _useModel(__props, 'count')`)
- expect(content).toMatch(`const toString = _useModel(__props, 'toString')`)
- expect(content).toMatch(`return { modelValue, c, toString }`)
- expect(content).not.toMatch('defineModel')
- expect(bindings).toStrictEqual({
- modelValue: BindingTypes.SETUP_REF,
- count: BindingTypes.PROPS,
- c: BindingTypes.SETUP_REF,
- toString: BindingTypes.SETUP_REF,
- })
- })
- test('w/ template literal name', () => {
- const { content, bindings } = compile(
- `
- <script setup>
- const x = defineModel(\`x\`, { default: 100 })
- const y = defineModel(\`y\`, { default: 200 })
- </script>
- `,
- )
- assertCode(content)
- expect(content).toMatch('"x": { default: 100 },')
- expect(content).toMatch('"y": { default: 200 },')
- expect(content).toMatch('emits: ["update:x", "update:y"],')
- expect(content).toMatch('const x = _useModel(__props, `x`)')
- expect(content).toMatch('const y = _useModel(__props, `y`)')
- expect(content).not.toMatch('defineModel')
- expect(bindings).toStrictEqual({
- x: BindingTypes.SETUP_REF,
- y: BindingTypes.SETUP_REF,
- })
- })
- test('w/ template literal name with expressions falls back to modelValue', () => {
- const { content } = compile(
- `
- <script setup>
- const name = 'x'
- const m = defineModel(\`\${name}\`)
- </script>
- `,
- )
- assertCode(content)
- expect(content).toMatch('"modelValue":')
- expect(content).toMatch('_useModel(__props, "modelValue",')
- })
- test('w/ defineProps and defineEmits', () => {
- const { content, bindings } = compile(
- `
- <script setup>
- defineProps({ foo: String })
- defineEmits(['change'])
- const count = defineModel({ default: 0 })
- </script>
- `,
- )
- assertCode(content)
- expect(content).toMatch(`props: /*@__PURE__*/_mergeModels({ foo: String }`)
- expect(content).toMatch(`"modelValue": { default: 0 }`)
- expect(content).toMatch(`const count = _useModel(__props, "modelValue")`)
- expect(content).not.toMatch('defineModel')
- expect(bindings).toStrictEqual({
- count: BindingTypes.SETUP_REF,
- foo: BindingTypes.PROPS,
- modelValue: BindingTypes.PROPS,
- })
- })
- test('w/ array props', () => {
- const { content, bindings } = compile(
- `
- <script setup>
- defineProps(['foo', 'bar'])
- const count = defineModel('count')
- </script>
- `,
- )
- assertCode(content)
- expect(content).toMatch(`props: /*@__PURE__*/_mergeModels(['foo', 'bar'], {
- "count": {},
- "countModifiers": {},
- })`)
- expect(content).toMatch(`const count = _useModel(__props, 'count')`)
- expect(content).not.toMatch('defineModel')
- expect(bindings).toStrictEqual({
- foo: BindingTypes.PROPS,
- bar: BindingTypes.PROPS,
- count: BindingTypes.SETUP_REF,
- })
- })
- test('w/ types, basic usage', () => {
- const { content, bindings } = compile(
- `
- <script setup lang="ts">
- const modelValue = defineModel<boolean | string>()
- const count = defineModel<number>('count')
- const disabled = defineModel<number>('disabled', { required: false })
- const any = defineModel<any | boolean>('any')
- </script>
- `,
- )
- assertCode(content)
- expect(content).toMatch('"modelValue": { type: [Boolean, String] }')
- expect(content).toMatch('"modelModifiers": {}')
- expect(content).toMatch('"count": { type: Number }')
- expect(content).toMatch(
- '"disabled": { type: Number, ...{ required: false } }',
- )
- expect(content).toMatch('"any": { type: Boolean, skipCheck: true }')
- expect(content).toMatch(
- 'emits: ["update:modelValue", "update:count", "update:disabled", "update:any"]',
- )
- expect(content).toMatch(
- `const modelValue = _useModel<boolean | string>(__props, "modelValue")`,
- )
- expect(content).toMatch(`const count = _useModel<number>(__props, 'count')`)
- expect(content).toMatch(
- `const disabled = _useModel<number>(__props, 'disabled')`,
- )
- expect(content).toMatch(
- `const any = _useModel<any | boolean>(__props, 'any')`,
- )
- expect(bindings).toStrictEqual({
- modelValue: BindingTypes.SETUP_REF,
- count: BindingTypes.SETUP_REF,
- disabled: BindingTypes.SETUP_REF,
- any: BindingTypes.SETUP_REF,
- })
- })
- test('w/ types, production mode', () => {
- const { content, bindings } = compile(
- `
- <script setup lang="ts">
- const modelValue = defineModel<boolean>()
- const fn = defineModel<() => void>('fn')
- const fnWithDefault = defineModel<() => void>('fnWithDefault', { default: () => null })
- const str = defineModel<string>('str')
- const optional = defineModel<string>('optional', { required: false })
- </script>
- `,
- { isProd: true },
- )
- assertCode(content)
- expect(content).toMatch('"modelValue": { type: Boolean }')
- expect(content).toMatch('"fn": {}')
- expect(content).toMatch(
- '"fnWithDefault": { type: Function, ...{ default: () => null } },',
- )
- expect(content).toMatch('"str": {}')
- expect(content).toMatch('"optional": { required: false }')
- expect(content).toMatch(
- 'emits: ["update:modelValue", "update:fn", "update:fnWithDefault", "update:str", "update:optional"]',
- )
- expect(content).toMatch(
- `const modelValue = _useModel<boolean>(__props, "modelValue")`,
- )
- expect(content).toMatch(`const fn = _useModel<() => void>(__props, 'fn')`)
- expect(content).toMatch(`const str = _useModel<string>(__props, 'str')`)
- expect(bindings).toStrictEqual({
- modelValue: BindingTypes.SETUP_REF,
- fn: BindingTypes.SETUP_REF,
- fnWithDefault: BindingTypes.SETUP_REF,
- str: BindingTypes.SETUP_REF,
- optional: BindingTypes.SETUP_REF,
- })
- })
- test('w/ types, production mode, boolean + multiple types', () => {
- const { content } = compile(
- `
- <script setup lang="ts">
- const modelValue = defineModel<boolean | string | {}>()
- </script>
- `,
- { isProd: true },
- )
- assertCode(content)
- expect(content).toMatch('"modelValue": { type: [Boolean, String, Object] }')
- })
- test('w/ types, production mode, function + runtime opts + multiple types', () => {
- const { content } = compile(
- `
- <script setup lang="ts">
- const modelValue = defineModel<number | (() => number)>({ default: () => 1 })
- </script>
- `,
- { isProd: true },
- )
- assertCode(content)
- expect(content).toMatch(
- '"modelValue": { type: [Number, Function], ...{ default: () => 1 } }',
- )
- })
- test('get / set transformers', () => {
- const { content } = compile(
- `
- <script setup lang="ts">
- const modelValue = defineModel({
- get(v) { return v - 1 },
- set: (v) => { return v + 1 },
- required: true
- })
- </script>
- `,
- )
- assertCode(content)
- expect(content).toMatch(/"modelValue": {\s+required: true,?\s+}/m)
- expect(content).toMatch(
- `_useModel(__props, "modelValue", {
- get(v) { return v - 1 },
- set: (v) => { return v + 1 },
- })`,
- )
- const { content: content2 } = compile(
- `
- <script setup lang="ts">
- const modelValue = defineModel({
- default: 0,
- get(v) { return v - 1 },
- required: true,
- set: (v) => { return v + 1 },
- })
- </script>
- `,
- )
- assertCode(content2)
- expect(content2).toMatch(
- /"modelValue": {\s+default: 0,\s+required: true,?\s+}/m,
- )
- expect(content2).toMatch(
- `_useModel(__props, "modelValue", {
- get(v) { return v - 1 },
- set: (v) => { return v + 1 },
- })`,
- )
- })
- test('usage w/ props destructure', () => {
- const { content } = compile(
- `
- <script setup lang="ts">
- const { x } = defineProps<{ x: number }>()
- const modelValue = defineModel({
- set: (v) => { return v + x }
- })
- </script>
- `,
- { propsDestructure: true },
- )
- assertCode(content)
- expect(content).toMatch(`set: (v) => { return v + __props.x }`)
- })
- test('w/ Boolean And Function types, production mode', () => {
- const { content, bindings } = compile(
- `
- <script setup lang="ts">
- const modelValue = defineModel<boolean | string>()
- </script>
- `,
- { isProd: true },
- )
- assertCode(content)
- expect(content).toMatch('"modelValue": { type: [Boolean, String] }')
- expect(content).toMatch('emits: ["update:modelValue"]')
- expect(content).toMatch(
- `const modelValue = _useModel<boolean | string>(__props, "modelValue")`,
- )
- expect(bindings).toStrictEqual({
- modelValue: BindingTypes.SETUP_REF,
- })
- })
- })
|