componentProxy.spec.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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', async () => {
  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. const nextTickThis = await instanceProxy.$nextTick(function(this: any) {
  96. return this
  97. })
  98. expect(nextTickThis).toBe(instanceProxy)
  99. })
  100. test('user attached properties', async () => {
  101. let instance: ComponentInternalInstance
  102. let instanceProxy: any
  103. const Comp = {
  104. setup() {
  105. return () => null
  106. },
  107. mounted() {
  108. instance = getCurrentInstance()!
  109. instanceProxy = this
  110. }
  111. }
  112. render(h(Comp), nodeOps.createElement('div'))
  113. instanceProxy.foo = 1
  114. expect(instanceProxy.foo).toBe(1)
  115. expect(instance!.ctx.foo).toBe(1)
  116. // should also allow properties that start with $
  117. const obj = (instanceProxy.$store = {})
  118. expect(instanceProxy.$store).toBe(obj)
  119. expect(instance!.ctx.$store).toBe(obj)
  120. })
  121. test('globalProperties', () => {
  122. let instance: ComponentInternalInstance
  123. let instanceProxy: any
  124. const Comp = {
  125. setup() {
  126. return () => null
  127. },
  128. mounted() {
  129. instance = getCurrentInstance()!
  130. instanceProxy = this
  131. }
  132. }
  133. const app = createApp(Comp)
  134. app.config.globalProperties.foo = 1
  135. app.mount(nodeOps.createElement('div'))
  136. expect(instanceProxy.foo).toBe(1)
  137. // set should overwrite globalProperties with local
  138. instanceProxy.foo = 2
  139. // expect(instanceProxy.foo).toBe(2)
  140. expect(instance!.ctx.foo).toBe(2)
  141. // should not affect global
  142. expect(app.config.globalProperties.foo).toBe(1)
  143. })
  144. test('has check', () => {
  145. let instanceProxy: any
  146. const Comp = {
  147. render() {},
  148. props: {
  149. msg: String
  150. },
  151. data() {
  152. return {
  153. foo: 0
  154. }
  155. },
  156. setup() {
  157. return {
  158. bar: 1
  159. }
  160. },
  161. mounted() {
  162. instanceProxy = this
  163. }
  164. }
  165. const app = createApp(Comp, { msg: 'hello' })
  166. app.config.globalProperties.global = 1
  167. app.mount(nodeOps.createElement('div'))
  168. // props
  169. expect('msg' in instanceProxy).toBe(true)
  170. // data
  171. expect('foo' in instanceProxy).toBe(true)
  172. // ctx
  173. expect('bar' in instanceProxy).toBe(true)
  174. // public properties
  175. expect('$el' in instanceProxy).toBe(true)
  176. // global properties
  177. expect('global' in instanceProxy).toBe(true)
  178. // non-existent
  179. expect('$foobar' in instanceProxy).toBe(false)
  180. expect('baz' in instanceProxy).toBe(false)
  181. // set non-existent (goes into proxyTarget sink)
  182. instanceProxy.baz = 1
  183. expect('baz' in instanceProxy).toBe(true)
  184. // dev mode ownKeys check for console inspection
  185. // should only expose own keys
  186. expect(Object.keys(instanceProxy)).toMatchObject([
  187. 'msg',
  188. 'bar',
  189. 'foo',
  190. 'baz'
  191. ])
  192. })
  193. // #864
  194. test('should not warn declared but absent props', () => {
  195. const Comp = {
  196. props: ['test'],
  197. render(this: any) {
  198. return this.test
  199. }
  200. }
  201. render(h(Comp), nodeOps.createElement('div'))
  202. expect(
  203. `was accessed during render but is not defined`
  204. ).not.toHaveBeenWarned()
  205. })
  206. test('should allow symbol to access on render', () => {
  207. const Comp = {
  208. render() {
  209. if ((this as any)[Symbol.unscopables]) {
  210. return '1'
  211. }
  212. return '2'
  213. }
  214. }
  215. const app = createApp(Comp)
  216. app.mount(nodeOps.createElement('div'))
  217. expect(
  218. `Property ${JSON.stringify(
  219. Symbol.unscopables
  220. )} was accessed during render ` + `but is not defined on instance.`
  221. ).toHaveBeenWarned()
  222. })
  223. })