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. </script>
  11. `,
  12. { defineModel: true }
  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('emits: ["update:modelValue", "update:count"],')
  19. expect(content).toMatch(
  20. `const modelValue = _useModel(__props, "modelValue")`
  21. )
  22. expect(content).toMatch(`const c = _useModel(__props, "count")`)
  23. expect(content).toMatch(`return { modelValue, c }`)
  24. expect(content).not.toMatch('defineModel')
  25. expect(bindings).toStrictEqual({
  26. modelValue: BindingTypes.SETUP_REF,
  27. count: BindingTypes.PROPS,
  28. c: BindingTypes.SETUP_REF
  29. })
  30. })
  31. test('w/ defineProps and defineEmits', () => {
  32. const { content, bindings } = compile(
  33. `
  34. <script setup>
  35. defineProps({ foo: String })
  36. defineEmits(['change'])
  37. const count = defineModel({ default: 0 })
  38. </script>
  39. `,
  40. { defineModel: true }
  41. )
  42. assertCode(content)
  43. expect(content).toMatch(`props: _mergeModels({ foo: String }`)
  44. expect(content).toMatch(`"modelValue": { default: 0 }`)
  45. expect(content).toMatch(`const count = _useModel(__props, "modelValue")`)
  46. expect(content).not.toMatch('defineModel')
  47. expect(bindings).toStrictEqual({
  48. count: BindingTypes.SETUP_REF,
  49. foo: BindingTypes.PROPS,
  50. modelValue: BindingTypes.PROPS
  51. })
  52. })
  53. test('w/ array props', () => {
  54. const { content, bindings } = compile(
  55. `
  56. <script setup>
  57. defineProps(['foo', 'bar'])
  58. const count = defineModel('count')
  59. </script>
  60. `,
  61. { defineModel: true }
  62. )
  63. assertCode(content)
  64. expect(content).toMatch(`props: _mergeModels(['foo', 'bar'], {
  65. "count": {},
  66. })`)
  67. expect(content).toMatch(`const count = _useModel(__props, "count")`)
  68. expect(content).not.toMatch('defineModel')
  69. expect(bindings).toStrictEqual({
  70. foo: BindingTypes.PROPS,
  71. bar: BindingTypes.PROPS,
  72. count: BindingTypes.SETUP_REF
  73. })
  74. })
  75. test('w/ local flag', () => {
  76. const { content } = compile(
  77. `<script setup>
  78. const foo = defineModel({ local: true, default: 1 })
  79. const bar = defineModel('bar', { [key]: true })
  80. const baz = defineModel('baz', { ...x })
  81. const qux = defineModel('qux', x)
  82. const foo2 = defineModel('foo2', { local: true, ...x })
  83. const local = true
  84. const hoist = defineModel('hoist', { local })
  85. </script>`,
  86. { defineModel: true }
  87. )
  88. assertCode(content)
  89. expect(content).toMatch(`_useModel(__props, "modelValue", { local: true })`)
  90. expect(content).toMatch(`_useModel(__props, "bar", { [key]: true })`)
  91. expect(content).toMatch(`_useModel(__props, "baz", { ...x })`)
  92. expect(content).toMatch(`_useModel(__props, "qux", x)`)
  93. expect(content).toMatch(`_useModel(__props, "foo2", { local: true })`)
  94. expect(content).toMatch(`_useModel(__props, "hoist", { local })`)
  95. })
  96. test('w/ types, basic usage', () => {
  97. const { content, bindings } = compile(
  98. `
  99. <script setup lang="ts">
  100. const modelValue = defineModel<boolean | string>()
  101. const count = defineModel<number>('count')
  102. const disabled = defineModel<number>('disabled', { required: false })
  103. const any = defineModel<any | boolean>('any')
  104. </script>
  105. `,
  106. { defineModel: true }
  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. { defineModel: true, 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. })