defineModel.spec.ts 6.1 KB

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