instance.spec.ts 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import { type Mock } from 'vitest'
  2. import Vue from '@vue/compat'
  3. import { Slots } from '../../runtime-core/src/componentSlots'
  4. import { Text } from '../../runtime-core/src/vnode'
  5. import {
  6. DeprecationTypes,
  7. deprecationData,
  8. toggleDeprecationWarning
  9. } from '../../runtime-core/src/compat/compatConfig'
  10. import { LegacyPublicInstance } from '../../runtime-core/src/compat/instance'
  11. beforeEach(() => {
  12. toggleDeprecationWarning(true)
  13. Vue.configureCompat({
  14. MODE: 2,
  15. GLOBAL_MOUNT: 'suppress-warning'
  16. })
  17. })
  18. afterEach(() => {
  19. toggleDeprecationWarning(false)
  20. Vue.configureCompat({ MODE: 3 })
  21. })
  22. test('INSTANCE_SET', () => {
  23. const obj: any = {}
  24. new Vue().$set(obj, 'foo', 1)
  25. expect(obj.foo).toBe(1)
  26. expect(
  27. deprecationData[DeprecationTypes.INSTANCE_SET].message
  28. ).toHaveBeenWarned()
  29. })
  30. test('INSTANCE_DELETE', () => {
  31. const obj: any = { foo: 1 }
  32. new Vue().$delete(obj, 'foo')
  33. expect('foo' in obj).toBe(false)
  34. expect(
  35. deprecationData[DeprecationTypes.INSTANCE_DELETE].message
  36. ).toHaveBeenWarned()
  37. })
  38. test('INSTANCE_DESTROY', () => {
  39. new Vue({ template: 'foo' }).$mount().$destroy()
  40. expect(
  41. deprecationData[DeprecationTypes.INSTANCE_DESTROY].message
  42. ).toHaveBeenWarned()
  43. })
  44. // https://github.com/vuejs/vue/blob/dev/test/unit/features/instance/methods-events.spec.js
  45. describe('INSTANCE_EVENT_EMITTER', () => {
  46. let vm: LegacyPublicInstance
  47. let spy: Mock
  48. beforeEach(() => {
  49. vm = new Vue()
  50. spy = vi.fn()
  51. })
  52. it('$on', () => {
  53. vm.$on('test', function (this: any) {
  54. // expect correct context
  55. expect(this).toBe(vm)
  56. spy.apply(this, arguments as unknown as any[])
  57. })
  58. vm.$emit('test', 1, 2, 3, 4)
  59. expect(spy).toHaveBeenCalledTimes(1)
  60. expect(spy).toHaveBeenCalledWith(1, 2, 3, 4)
  61. expect(
  62. deprecationData[DeprecationTypes.INSTANCE_EVENT_EMITTER].message
  63. ).toHaveBeenWarned()
  64. })
  65. it('$on multi event', () => {
  66. vm.$on(['test1', 'test2'], function (this: any) {
  67. expect(this).toBe(vm)
  68. spy.apply(this, arguments as unknown as any[])
  69. })
  70. vm.$emit('test1', 1, 2, 3, 4)
  71. expect(spy).toHaveBeenCalledTimes(1)
  72. expect(spy).toHaveBeenCalledWith(1, 2, 3, 4)
  73. vm.$emit('test2', 5, 6, 7, 8)
  74. expect(spy).toHaveBeenCalledTimes(2)
  75. expect(spy).toHaveBeenCalledWith(5, 6, 7, 8)
  76. expect(
  77. deprecationData[DeprecationTypes.INSTANCE_EVENT_EMITTER].message
  78. ).toHaveBeenWarned()
  79. })
  80. it('$off multi event', () => {
  81. vm.$on(['test1', 'test2', 'test3'], spy)
  82. vm.$off(['test1', 'test2'], spy)
  83. vm.$emit('test1')
  84. vm.$emit('test2')
  85. expect(spy).not.toHaveBeenCalled()
  86. vm.$emit('test3', 1, 2, 3, 4)
  87. expect(spy).toHaveBeenCalledTimes(1)
  88. expect(
  89. deprecationData[DeprecationTypes.INSTANCE_EVENT_EMITTER].message
  90. ).toHaveBeenWarned()
  91. })
  92. it('$off multi event without callback', () => {
  93. vm.$on(['test1', 'test2'], spy)
  94. vm.$off(['test1', 'test2'])
  95. vm.$emit('test1')
  96. expect(spy).not.toHaveBeenCalled()
  97. expect(
  98. deprecationData[DeprecationTypes.INSTANCE_EVENT_EMITTER].message
  99. ).toHaveBeenWarned()
  100. })
  101. it('$once', () => {
  102. vm.$once('test', spy)
  103. vm.$emit('test', 1, 2, 3)
  104. vm.$emit('test', 2, 3, 4)
  105. expect(spy).toHaveBeenCalledTimes(1)
  106. expect(spy).toHaveBeenCalledWith(1, 2, 3)
  107. expect(
  108. deprecationData[DeprecationTypes.INSTANCE_EVENT_EMITTER].message
  109. ).toHaveBeenWarned()
  110. })
  111. it('$off event added by $once', () => {
  112. vm.$once('test', spy)
  113. vm.$off('test', spy) // test off event and this event added by once
  114. vm.$emit('test', 1, 2, 3)
  115. expect(spy).not.toHaveBeenCalled()
  116. expect(
  117. deprecationData[DeprecationTypes.INSTANCE_EVENT_EMITTER].message
  118. ).toHaveBeenWarned()
  119. })
  120. it('$off', () => {
  121. vm.$on('test1', spy)
  122. vm.$on('test2', spy)
  123. vm.$off()
  124. vm.$emit('test1')
  125. vm.$emit('test2')
  126. expect(spy).not.toHaveBeenCalled()
  127. expect(
  128. deprecationData[DeprecationTypes.INSTANCE_EVENT_EMITTER].message
  129. ).toHaveBeenWarned()
  130. })
  131. it('$off event', () => {
  132. vm.$on('test1', spy)
  133. vm.$on('test2', spy)
  134. vm.$off('test1')
  135. vm.$off('test1') // test off something that's already off
  136. vm.$emit('test1', 1)
  137. vm.$emit('test2', 2)
  138. expect(spy).toHaveBeenCalledTimes(1)
  139. expect(spy).toHaveBeenCalledWith(2)
  140. expect(
  141. deprecationData[DeprecationTypes.INSTANCE_EVENT_EMITTER].message
  142. ).toHaveBeenWarned()
  143. })
  144. it('$off event + fn', () => {
  145. const spy2 = vi.fn()
  146. vm.$on('test', spy)
  147. vm.$on('test', spy2)
  148. vm.$off('test', spy)
  149. vm.$emit('test', 1, 2, 3)
  150. expect(spy).not.toHaveBeenCalled()
  151. expect(spy2).toHaveBeenCalledTimes(1)
  152. expect(spy2).toHaveBeenCalledWith(1, 2, 3)
  153. expect(
  154. deprecationData[DeprecationTypes.INSTANCE_EVENT_EMITTER].message
  155. ).toHaveBeenWarned()
  156. })
  157. })
  158. describe('INSTANCE_EVENT_HOOKS', () => {
  159. test('instance API', () => {
  160. const spy = vi.fn()
  161. const vm = new Vue({ template: 'foo' })
  162. vm.$on('hook:mounted', spy)
  163. vm.$mount()
  164. expect(spy).toHaveBeenCalled()
  165. expect(
  166. (
  167. deprecationData[DeprecationTypes.INSTANCE_EVENT_HOOKS]
  168. .message as Function
  169. )('hook:mounted')
  170. ).toHaveBeenWarned()
  171. })
  172. test('via template', () => {
  173. const spy = vi.fn()
  174. new Vue({
  175. template: `<child @hook:mounted="spy"/>`,
  176. methods: { spy },
  177. components: {
  178. child: {
  179. template: 'foo'
  180. }
  181. }
  182. }).$mount()
  183. expect(spy).toHaveBeenCalled()
  184. expect(
  185. (
  186. deprecationData[DeprecationTypes.INSTANCE_EVENT_HOOKS]
  187. .message as Function
  188. )('hook:mounted')
  189. ).toHaveBeenWarned()
  190. })
  191. })
  192. test('INSTANCE_EVENT_CHILDREN', () => {
  193. const vm = new Vue({
  194. template: `<child/><div><child v-for="i in 3"/></div>`,
  195. components: {
  196. child: {
  197. template: 'foo',
  198. data() {
  199. return { n: 1 }
  200. }
  201. }
  202. }
  203. }).$mount()
  204. expect(vm.$children.length).toBe(4)
  205. vm.$children.forEach((c: any) => {
  206. expect(c.n).toBe(1)
  207. })
  208. expect(
  209. deprecationData[DeprecationTypes.INSTANCE_CHILDREN].message
  210. ).toHaveBeenWarned()
  211. })
  212. test('INSTANCE_LISTENERS', () => {
  213. const foo = () => 'foo'
  214. const bar = () => 'bar'
  215. let listeners: Record<string, Function>
  216. new Vue({
  217. template: `<child @click="foo" @custom="bar" />`,
  218. methods: { foo, bar },
  219. components: {
  220. child: {
  221. template: `<div/>`,
  222. mounted() {
  223. listeners = this.$listeners
  224. }
  225. }
  226. }
  227. }).$mount()
  228. expect(Object.keys(listeners!)).toMatchObject(['click', 'custom'])
  229. expect(listeners!.click()).toBe('foo')
  230. expect(listeners!.custom()).toBe('bar')
  231. expect(
  232. deprecationData[DeprecationTypes.INSTANCE_LISTENERS].message
  233. ).toHaveBeenWarned()
  234. })
  235. describe('INSTANCE_SCOPED_SLOTS', () => {
  236. test('explicit usage', () => {
  237. let slots: Slots
  238. new Vue({
  239. template: `<child v-slot="{ msg }">{{ msg }}</child>`,
  240. components: {
  241. child: {
  242. compatConfig: { RENDER_FUNCTION: false },
  243. render() {
  244. slots = this.$scopedSlots
  245. }
  246. }
  247. }
  248. }).$mount()
  249. expect(slots!.default!({ msg: 'hi' })).toMatchObject([
  250. {
  251. type: Text,
  252. children: 'hi'
  253. }
  254. ])
  255. expect(
  256. deprecationData[DeprecationTypes.INSTANCE_SCOPED_SLOTS].message
  257. ).toHaveBeenWarned()
  258. })
  259. test('should not include legacy slot usage in $scopedSlots', () => {
  260. let normalSlots: Slots
  261. let scopedSlots: Slots
  262. new Vue({
  263. template: `<child><div>default</div></child>`,
  264. components: {
  265. child: {
  266. compatConfig: { RENDER_FUNCTION: false },
  267. render() {
  268. normalSlots = this.$slots
  269. scopedSlots = this.$scopedSlots
  270. }
  271. }
  272. }
  273. }).$mount()
  274. expect('default' in normalSlots!).toBe(true)
  275. expect('default' in scopedSlots!).toBe(false)
  276. expect(
  277. deprecationData[DeprecationTypes.INSTANCE_SCOPED_SLOTS].message
  278. ).toHaveBeenWarned()
  279. })
  280. })
  281. test('INSTANCE_ATTR_CLASS_STYLE', () => {
  282. const vm = new Vue({
  283. template: `<child class="foo" style="color:red" id="ok" />`,
  284. components: {
  285. child: {
  286. inheritAttrs: false,
  287. template: `<div><div v-bind="$attrs" /></div>`
  288. }
  289. }
  290. }).$mount()
  291. expect(vm.$el).toBeInstanceOf(HTMLDivElement)
  292. expect(vm.$el.outerHTML).toBe(
  293. `<div class="foo" style="color: red;"><div id="ok"></div></div>`
  294. )
  295. expect(
  296. (
  297. deprecationData[DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE]
  298. .message as Function
  299. )('Anonymous')
  300. ).toHaveBeenWarned()
  301. })