componentProxy.spec.ts 5.9 KB

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