defineModel.spec.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { BindingTypes } from '@vue/compiler-core'
  2. import { compileSFCScript as compile, assertCode } from '../utils'
  3. describe('defineModel()', () => {
  4. test('basic usage', () => {
  5. const { content, bindings } = compile(
  6. `
  7. <script setup>
  8. const modelValue = defineModel({ required: true })
  9. const c = defineModel('count')
  10. const toString = defineModel('toString', { type: Function })
  11. </script>
  12. `,
  13. { defineModel: true }
  14. )
  15. assertCode(content)
  16. expect(content).toMatch('props: {')
  17. expect(content).toMatch('"modelValue": { required: true },')
  18. expect(content).toMatch('"count": {},')
  19. expect(content).toMatch('"toString": { type: Function },')
  20. expect(content).toMatch(
  21. 'emits: ["update:modelValue", "update:count", "update:toString"],'
  22. )
  23. expect(content).toMatch(
  24. `const modelValue = _useModel(__props, "modelValue")`
  25. )
  26. expect(content).toMatch(`const c = _useModel(__props, "count")`)
  27. expect(content).toMatch(`return { modelValue, c, toString }`)
  28. expect(content).not.toMatch('defineModel')
  29. expect(bindings).toStrictEqual({
  30. modelValue: BindingTypes.SETUP_REF,
  31. count: BindingTypes.PROPS,
  32. c: BindingTypes.SETUP_REF,
  33. toString: BindingTypes.SETUP_REF
  34. })
  35. })
  36. test('w/ defineProps and defineEmits', () => {
  37. const { content, bindings } = compile(
  38. `
  39. <script setup>
  40. defineProps({ foo: String })
  41. defineEmits(['change'])
  42. const count = defineModel({ default: 0 })
  43. </script>
  44. `,
  45. { defineModel: true }
  46. )
  47. assertCode(content)
  48. expect(content).toMatch(`props: _mergeModels({ foo: String }`)
  49. expect(content).toMatch(`"modelValue": { default: 0 }`)
  50. expect(content).toMatch(`const count = _useModel(__props, "modelValue")`)
  51. expect(content).not.toMatch('defineModel')
  52. expect(bindings).toStrictEqual({
  53. count: BindingTypes.SETUP_REF,
  54. foo: BindingTypes.PROPS,
  55. modelValue: BindingTypes.PROPS
  56. })
  57. })
  58. test('w/ array props', () => {
  59. const { content, bindings } = compile(
  60. `
  61. <script setup>
  62. defineProps(['foo', 'bar'])
  63. const count = defineModel('count')
  64. </script>
  65. `,
  66. { defineModel: true }
  67. )
  68. assertCode(content)
  69. expect(content).toMatch(`props: _mergeModels(['foo', 'bar'], {
  70. "count": {},
  71. })`)
  72. expect(content).toMatch(`const count = _useModel(__props, "count")`)
  73. expect(content).not.toMatch('defineModel')
  74. expect(bindings).toStrictEqual({
  75. foo: BindingTypes.PROPS,
  76. bar: BindingTypes.PROPS,
  77. count: BindingTypes.SETUP_REF
  78. })
  79. })
  80. test('w/ local flag', () => {
  81. const { content } = compile(
  82. `<script setup>
  83. const foo = defineModel({ local: true, default: 1 })
  84. const bar = defineModel('bar', { [key]: true })
  85. const baz = defineModel('baz', { ...x })
  86. const qux = defineModel('qux', x)
  87. const foo2 = defineModel('foo2', { local: true, ...x })
  88. const local = true
  89. const hoist = defineModel('hoist', { local })
  90. </script>`,
  91. { defineModel: true }
  92. )
  93. assertCode(content)
  94. expect(content).toMatch(`_useModel(__props, "modelValue", { local: true })`)
  95. expect(content).toMatch(`_useModel(__props, "bar", { [key]: true })`)
  96. expect(content).toMatch(`_useModel(__props, "baz", { ...x })`)
  97. expect(content).toMatch(`_useModel(__props, "qux", x)`)
  98. expect(content).toMatch(`_useModel(__props, "foo2", { local: true })`)
  99. expect(content).toMatch(`_useModel(__props, "hoist", { local })`)
  100. })
  101. test('w/ types, basic usage', () => {
  102. const { content, bindings } = compile(
  103. `
  104. <script setup lang="ts">
  105. const modelValue = defineModel<boolean | string>()
  106. const count = defineModel<number>('count')
  107. const disabled = defineModel<number>('disabled', { required: false })
  108. const any = defineModel<any | boolean>('any')
  109. </script>
  110. `,
  111. { defineModel: true }
  112. )
  113. assertCode(content)
  114. expect(content).toMatch('"modelValue": { type: [Boolean, String] }')
  115. expect(content).toMatch('"count": { type: Number }')
  116. expect(content).toMatch(
  117. '"disabled": { type: Number, ...{ required: false } }'
  118. )
  119. expect(content).toMatch('"any": { type: Boolean, skipCheck: true }')
  120. expect(content).toMatch(
  121. 'emits: ["update:modelValue", "update:count", "update:disabled", "update:any"]'
  122. )
  123. expect(content).toMatch(
  124. `const modelValue = _useModel(__props, "modelValue")`
  125. )
  126. expect(content).toMatch(`const count = _useModel(__props, "count")`)
  127. expect(content).toMatch(`const disabled = _useModel(__props, "disabled")`)
  128. expect(content).toMatch(`const any = _useModel(__props, "any")`)
  129. expect(bindings).toStrictEqual({
  130. modelValue: BindingTypes.SETUP_REF,
  131. count: BindingTypes.SETUP_REF,
  132. disabled: BindingTypes.SETUP_REF,
  133. any: BindingTypes.SETUP_REF
  134. })
  135. })
  136. test('w/ types, production mode', () => {
  137. const { content, bindings } = compile(
  138. `
  139. <script setup lang="ts">
  140. const modelValue = defineModel<boolean>()
  141. const fn = defineModel<() => void>('fn')
  142. const fnWithDefault = defineModel<() => void>('fnWithDefault', { default: () => null })
  143. const str = defineModel<string>('str')
  144. const optional = defineModel<string>('optional', { required: false })
  145. </script>
  146. `,
  147. { defineModel: true, isProd: true }
  148. )
  149. assertCode(content)
  150. expect(content).toMatch('"modelValue": { type: Boolean }')
  151. expect(content).toMatch('"fn": {}')
  152. expect(content).toMatch(
  153. '"fnWithDefault": { type: Function, ...{ default: () => null } },'
  154. )
  155. expect(content).toMatch('"str": {}')
  156. expect(content).toMatch('"optional": { required: false }')
  157. expect(content).toMatch(
  158. 'emits: ["update:modelValue", "update:fn", "update:fnWithDefault", "update:str", "update:optional"]'
  159. )
  160. expect(content).toMatch(
  161. `const modelValue = _useModel(__props, "modelValue")`
  162. )
  163. expect(content).toMatch(`const fn = _useModel(__props, "fn")`)
  164. expect(content).toMatch(`const str = _useModel(__props, "str")`)
  165. expect(bindings).toStrictEqual({
  166. modelValue: BindingTypes.SETUP_REF,
  167. fn: BindingTypes.SETUP_REF,
  168. fnWithDefault: BindingTypes.SETUP_REF,
  169. str: BindingTypes.SETUP_REF,
  170. optional: BindingTypes.SETUP_REF
  171. })
  172. })
  173. })