componentProxy.spec.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import {
  2. h,
  3. render,
  4. getCurrentInstance,
  5. nodeOps,
  6. createApp,
  7. shallowReadonly
  8. } from '@vue/runtime-test'
  9. import { mockWarn } from '@vue/shared'
  10. import { ComponentInternalInstance } from '../src/component'
  11. describe('component: proxy', () => {
  12. mockWarn()
  13. test('data', () => {
  14. let instance: ComponentInternalInstance
  15. let instanceProxy: any
  16. const Comp = {
  17. data() {
  18. return {
  19. foo: 1
  20. }
  21. },
  22. mounted() {
  23. instance = getCurrentInstance()!
  24. instanceProxy = this
  25. },
  26. render() {
  27. return null
  28. }
  29. }
  30. render(h(Comp), nodeOps.createElement('div'))
  31. expect(instanceProxy.foo).toBe(1)
  32. instanceProxy.foo = 2
  33. expect(instance!.data.foo).toBe(2)
  34. })
  35. test('setupState', () => {
  36. let instance: ComponentInternalInstance
  37. let instanceProxy: any
  38. const Comp = {
  39. setup() {
  40. return {
  41. foo: 1
  42. }
  43. },
  44. mounted() {
  45. instance = getCurrentInstance()!
  46. instanceProxy = this
  47. },
  48. render() {
  49. return null
  50. }
  51. }
  52. render(h(Comp), nodeOps.createElement('div'))
  53. expect(instanceProxy.foo).toBe(1)
  54. instanceProxy.foo = 2
  55. expect(instance!.setupState.foo).toBe(2)
  56. })
  57. test('should not expose non-declared props', () => {
  58. let instanceProxy: any
  59. const Comp = {
  60. setup() {
  61. return () => null
  62. },
  63. mounted() {
  64. instanceProxy = this
  65. }
  66. }
  67. render(h(Comp, { count: 1 }), nodeOps.createElement('div'))
  68. expect('count' in instanceProxy).toBe(false)
  69. })
  70. test('public properties', () => {
  71. let instance: ComponentInternalInstance
  72. let instanceProxy: any
  73. const Comp = {
  74. setup() {
  75. return () => null
  76. },
  77. mounted() {
  78. instance = getCurrentInstance()!
  79. instanceProxy = this
  80. }
  81. }
  82. render(h(Comp), nodeOps.createElement('div'))
  83. expect(instanceProxy.$data).toBe(instance!.data)
  84. expect(instanceProxy.$props).toBe(shallowReadonly(instance!.props))
  85. expect(instanceProxy.$attrs).toBe(shallowReadonly(instance!.attrs))
  86. expect(instanceProxy.$slots).toBe(shallowReadonly(instance!.slots))
  87. expect(instanceProxy.$refs).toBe(shallowReadonly(instance!.refs))
  88. expect(instanceProxy.$parent).toBe(
  89. instance!.parent && instance!.parent.proxy
  90. )
  91. expect(instanceProxy.$root).toBe(instance!.root.proxy)
  92. expect(instanceProxy.$emit).toBe(instance!.emit)
  93. expect(instanceProxy.$el).toBe(instance!.vnode.el)
  94. expect(instanceProxy.$options).toBe(instance!.type)
  95. expect(() => (instanceProxy.$data = {})).toThrow(TypeError)
  96. expect(`Attempting to mutate public property "$data"`).toHaveBeenWarned()
  97. })
  98. test('user attached properties', async () => {
  99. let instance: ComponentInternalInstance
  100. let instanceProxy: any
  101. const Comp = {
  102. setup() {
  103. return () => null
  104. },
  105. mounted() {
  106. instance = getCurrentInstance()!
  107. instanceProxy = this
  108. }
  109. }
  110. render(h(Comp), nodeOps.createElement('div'))
  111. instanceProxy.foo = 1
  112. expect(instanceProxy.foo).toBe(1)
  113. expect(instance!.ctx.foo).toBe(1)
  114. // should also allow properties that start with $
  115. const obj = (instanceProxy.$store = {})
  116. expect(instanceProxy.$store).toBe(obj)
  117. expect(instance!.ctx.$store).toBe(obj)
  118. })
  119. test('globalProperties', () => {
  120. let instance: ComponentInternalInstance
  121. let instanceProxy: any
  122. const Comp = {
  123. setup() {
  124. return () => null
  125. },
  126. mounted() {
  127. instance = getCurrentInstance()!
  128. instanceProxy = this
  129. }
  130. }
  131. const app = createApp(Comp)
  132. app.config.globalProperties.foo = 1
  133. app.mount(nodeOps.createElement('div'))
  134. expect(instanceProxy.foo).toBe(1)
  135. // set should overwrite globalProperties with local
  136. instanceProxy.foo = 2
  137. // expect(instanceProxy.foo).toBe(2)
  138. expect(instance!.ctx.foo).toBe(2)
  139. // should not affect global
  140. expect(app.config.globalProperties.foo).toBe(1)
  141. })
  142. test('has check', () => {
  143. let instanceProxy: any
  144. const Comp = {
  145. render() {},
  146. props: {
  147. msg: String
  148. },
  149. data() {
  150. return {
  151. foo: 0
  152. }
  153. },
  154. setup() {
  155. return {
  156. bar: 1
  157. }
  158. },
  159. mounted() {
  160. instanceProxy = this
  161. }
  162. }
  163. const app = createApp(Comp, { msg: 'hello' })
  164. app.config.globalProperties.global = 1
  165. app.mount(nodeOps.createElement('div'))
  166. // props
  167. expect('msg' in instanceProxy).toBe(true)
  168. // data
  169. expect('foo' in instanceProxy).toBe(true)
  170. // ctx
  171. expect('bar' in instanceProxy).toBe(true)
  172. // public properties
  173. expect('$el' in instanceProxy).toBe(true)
  174. // global properties
  175. expect('global' in instanceProxy).toBe(true)
  176. // non-existent
  177. expect('$foobar' in instanceProxy).toBe(false)
  178. expect('baz' in instanceProxy).toBe(false)
  179. // set non-existent (goes into proxyTarget sink)
  180. instanceProxy.baz = 1
  181. expect('baz' in instanceProxy).toBe(true)
  182. // dev mode ownKeys check for console inspection
  183. // should only expose own keys
  184. expect(Object.keys(instanceProxy)).toMatchObject([
  185. 'msg',
  186. 'bar',
  187. 'foo',
  188. 'baz'
  189. ])
  190. })
  191. // #864
  192. test('should not warn declared but absent props', () => {
  193. const Comp = {
  194. props: ['test'],
  195. render(this: any) {
  196. return this.test
  197. }
  198. }
  199. render(h(Comp), nodeOps.createElement('div'))
  200. expect(
  201. `was accessed during render but is not defined`
  202. ).not.toHaveBeenWarned()
  203. })
  204. })