componentVModel.spec.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import Vue from '@vue/compat'
  2. import type { ComponentOptions } from '../../runtime-core/src/component'
  3. import { nextTick } from '../../runtime-core/src/scheduler'
  4. import {
  5. DeprecationTypes,
  6. deprecationData,
  7. toggleDeprecationWarning,
  8. } from '../../runtime-core/src/compat/compatConfig'
  9. import { triggerEvent } from './utils'
  10. beforeEach(() => {
  11. toggleDeprecationWarning(true)
  12. Vue.configureCompat({
  13. MODE: 2,
  14. GLOBAL_MOUNT: 'suppress-warning',
  15. })
  16. })
  17. afterEach(() => {
  18. toggleDeprecationWarning(false)
  19. Vue.configureCompat({ MODE: 3 })
  20. })
  21. describe('COMPONENT_V_MODEL', () => {
  22. async function runTest(CustomInput: ComponentOptions) {
  23. const vm = new Vue({
  24. data() {
  25. return {
  26. text: 'foo',
  27. }
  28. },
  29. components: { CustomInput },
  30. template: `
  31. <div>
  32. <span>{{ text }}</span>
  33. <custom-input v-model="text"></custom-input>
  34. </div>
  35. `,
  36. }).$mount() as any
  37. const input = vm.$el.querySelector('input')
  38. const span = vm.$el.querySelector('span')
  39. expect(input.value).toBe('foo')
  40. expect(span.textContent).toBe('foo')
  41. expect(
  42. (deprecationData[DeprecationTypes.COMPONENT_V_MODEL].message as Function)(
  43. CustomInput,
  44. ),
  45. ).toHaveBeenWarned()
  46. input.value = 'bar'
  47. triggerEvent(input, 'input')
  48. await nextTick()
  49. expect(input.value).toBe('bar')
  50. expect(span.textContent).toBe('bar')
  51. vm.text = 'baz'
  52. await nextTick()
  53. expect(input.value).toBe('baz')
  54. expect(span.textContent).toBe('baz')
  55. }
  56. test('basic usage', async () => {
  57. await runTest({
  58. name: 'CustomInput',
  59. props: ['value'],
  60. template: `<input :value="value" @input="$emit('input', $event.target.value)">`,
  61. })
  62. })
  63. test('with model option', async () => {
  64. await runTest({
  65. name: 'CustomInput',
  66. props: ['input'],
  67. model: {
  68. prop: 'input',
  69. event: 'update',
  70. },
  71. template: `<input :value="input" @input="$emit('update', $event.target.value)">`,
  72. })
  73. })
  74. async function runTestWithModifier(CustomInput: ComponentOptions) {
  75. const vm = new Vue({
  76. data() {
  77. return {
  78. text: ' foo ',
  79. }
  80. },
  81. components: {
  82. CustomInput,
  83. },
  84. template: `
  85. <div>
  86. <span>{{ text }}</span>
  87. <custom-input v-model.trim="text"></custom-input>
  88. </div>
  89. `,
  90. }).$mount() as any
  91. const input = vm.$el.querySelector('input')
  92. const span = vm.$el.querySelector('span')
  93. expect(input.value).toBe(' foo ')
  94. expect(span.textContent).toBe(' foo ')
  95. expect(
  96. (deprecationData[DeprecationTypes.COMPONENT_V_MODEL].message as Function)(
  97. CustomInput,
  98. ),
  99. ).toHaveBeenWarned()
  100. input.value = ' bar '
  101. triggerEvent(input, 'input')
  102. await nextTick()
  103. expect(input.value).toBe('bar')
  104. expect(span.textContent).toBe('bar')
  105. }
  106. test('with model modifiers', async () => {
  107. await runTestWithModifier({
  108. name: 'CustomInput',
  109. props: ['value'],
  110. template: `<input :value="value" @input="$emit('input', $event.target.value)">`,
  111. })
  112. })
  113. test('with model modifiers and model option', async () => {
  114. await runTestWithModifier({
  115. name: 'CustomInput',
  116. props: ['foo'],
  117. model: {
  118. prop: 'foo',
  119. event: 'bar',
  120. },
  121. template: `<input :value="foo" @input="$emit('bar', $event.target.value)">`,
  122. })
  123. })
  124. // #14202
  125. test('should handle v-model deprecation warning with missing appContext', async () => {
  126. const ChildComponent = {
  127. template: `<div @click="$emit('input', 'new val')">{{ value }}</div>`,
  128. props: ['value'],
  129. }
  130. const vm = new Vue({
  131. components: { ChildComponent },
  132. data() {
  133. return {
  134. myVal: 'initial',
  135. }
  136. },
  137. template: `
  138. <div>
  139. <child-component v-model="myVal"></child-component>
  140. </div>
  141. `,
  142. }).$mount() as any
  143. expect(vm.$el.textContent).toContain('initial')
  144. expect(
  145. (deprecationData[DeprecationTypes.COMPONENT_V_MODEL].message as Function)(
  146. ChildComponent,
  147. ),
  148. ).toHaveBeenWarned()
  149. // Should work correctly
  150. const child = vm.$el.querySelector('div')
  151. child.click()
  152. await nextTick()
  153. expect(vm.myVal).toBe('new val')
  154. expect(vm.$el.textContent).toContain('new val')
  155. })
  156. })