defineModel.spec.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import { BindingTypes } from '@vue/compiler-core'
  2. import { assertCode, compileSFCScript as compile } 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. "countModifiers": {},
  69. })`)
  70. expect(content).toMatch(`const count = _useModel(__props, "count")`)
  71. expect(content).not.toMatch('defineModel')
  72. expect(bindings).toStrictEqual({
  73. foo: BindingTypes.PROPS,
  74. bar: BindingTypes.PROPS,
  75. count: BindingTypes.SETUP_REF,
  76. })
  77. })
  78. test('w/ types, basic usage', () => {
  79. const { content, bindings } = compile(
  80. `
  81. <script setup lang="ts">
  82. const modelValue = defineModel<boolean | string>()
  83. const count = defineModel<number>('count')
  84. const disabled = defineModel<number>('disabled', { required: false })
  85. const any = defineModel<any | boolean>('any')
  86. </script>
  87. `,
  88. )
  89. assertCode(content)
  90. expect(content).toMatch('"modelValue": { type: [Boolean, String] }')
  91. expect(content).toMatch('"modelModifiers": {}')
  92. expect(content).toMatch('"count": { type: Number }')
  93. expect(content).toMatch(
  94. '"disabled": { type: Number, ...{ required: false } }',
  95. )
  96. expect(content).toMatch('"any": { type: Boolean, skipCheck: true }')
  97. expect(content).toMatch(
  98. 'emits: ["update:modelValue", "update:count", "update:disabled", "update:any"]',
  99. )
  100. expect(content).toMatch(
  101. `const modelValue = _useModel(__props, "modelValue")`,
  102. )
  103. expect(content).toMatch(`const count = _useModel(__props, "count")`)
  104. expect(content).toMatch(`const disabled = _useModel(__props, "disabled")`)
  105. expect(content).toMatch(`const any = _useModel(__props, "any")`)
  106. expect(bindings).toStrictEqual({
  107. modelValue: BindingTypes.SETUP_REF,
  108. count: BindingTypes.SETUP_REF,
  109. disabled: BindingTypes.SETUP_REF,
  110. any: BindingTypes.SETUP_REF,
  111. })
  112. })
  113. test('w/ types, production mode', () => {
  114. const { content, bindings } = compile(
  115. `
  116. <script setup lang="ts">
  117. const modelValue = defineModel<boolean>()
  118. const fn = defineModel<() => void>('fn')
  119. const fnWithDefault = defineModel<() => void>('fnWithDefault', { default: () => null })
  120. const str = defineModel<string>('str')
  121. const optional = defineModel<string>('optional', { required: false })
  122. </script>
  123. `,
  124. { isProd: true },
  125. )
  126. assertCode(content)
  127. expect(content).toMatch('"modelValue": { type: Boolean }')
  128. expect(content).toMatch('"fn": {}')
  129. expect(content).toMatch(
  130. '"fnWithDefault": { type: Function, ...{ default: () => null } },',
  131. )
  132. expect(content).toMatch('"str": {}')
  133. expect(content).toMatch('"optional": { required: false }')
  134. expect(content).toMatch(
  135. 'emits: ["update:modelValue", "update:fn", "update:fnWithDefault", "update:str", "update:optional"]',
  136. )
  137. expect(content).toMatch(
  138. `const modelValue = _useModel(__props, "modelValue")`,
  139. )
  140. expect(content).toMatch(`const fn = _useModel(__props, "fn")`)
  141. expect(content).toMatch(`const str = _useModel(__props, "str")`)
  142. expect(bindings).toStrictEqual({
  143. modelValue: BindingTypes.SETUP_REF,
  144. fn: BindingTypes.SETUP_REF,
  145. fnWithDefault: BindingTypes.SETUP_REF,
  146. str: BindingTypes.SETUP_REF,
  147. optional: BindingTypes.SETUP_REF,
  148. })
  149. })
  150. test('get / set transformers', () => {
  151. const { content } = compile(
  152. `
  153. <script setup lang="ts">
  154. const modelValue = defineModel({
  155. get(v) { return v - 1 },
  156. set: (v) => { return v + 1 },
  157. required: true
  158. })
  159. </script>
  160. `,
  161. )
  162. assertCode(content)
  163. expect(content).toMatch(/"modelValue": {\s+required: true,?\s+}/m)
  164. expect(content).toMatch(
  165. `_useModel(__props, "modelValue", { get(v) { return v - 1 }, set: (v) => { return v + 1 }, })`,
  166. )
  167. const { content: content2 } = compile(
  168. `
  169. <script setup lang="ts">
  170. const modelValue = defineModel({
  171. default: 0,
  172. get(v) { return v - 1 },
  173. required: true,
  174. set: (v) => { return v + 1 },
  175. })
  176. </script>
  177. `,
  178. )
  179. assertCode(content2)
  180. expect(content2).toMatch(
  181. /"modelValue": {\s+default: 0,\s+required: true,?\s+}/m,
  182. )
  183. expect(content2).toMatch(
  184. `_useModel(__props, "modelValue", { get(v) { return v - 1 }, set: (v) => { return v + 1 }, })`,
  185. )
  186. })
  187. })