reactive.spec.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { reactive, isReactive, toRaw, markNonReactive } from '../src/reactive'
  2. import { mockWarn } from '@vue/runtime-test'
  3. describe('reactivity/reactive', () => {
  4. mockWarn()
  5. test('Object', () => {
  6. const original = { foo: 1 }
  7. const observed = reactive(original)
  8. expect(observed).not.toBe(original)
  9. expect(isReactive(observed)).toBe(true)
  10. expect(isReactive(original)).toBe(false)
  11. // get
  12. expect(observed.foo).toBe(1)
  13. // has
  14. expect('foo' in observed).toBe(true)
  15. // ownKeys
  16. expect(Object.keys(observed)).toEqual(['foo'])
  17. })
  18. test('Array', () => {
  19. const original = [{ foo: 1 }]
  20. const observed = reactive(original)
  21. expect(observed).not.toBe(original)
  22. expect(isReactive(observed)).toBe(true)
  23. expect(isReactive(original)).toBe(false)
  24. expect(isReactive(observed[0])).toBe(true)
  25. // get
  26. expect(observed[0].foo).toBe(1)
  27. // has
  28. expect(0 in observed).toBe(true)
  29. // ownKeys
  30. expect(Object.keys(observed)).toEqual(['0'])
  31. })
  32. test('cloned reactive Array should point to observed values', () => {
  33. const original = [{ foo: 1 }]
  34. const observed = reactive(original)
  35. const clone = observed.slice()
  36. expect(isReactive(clone[0])).toBe(true)
  37. expect(clone[0]).not.toBe(original[0])
  38. expect(clone[0]).toBe(observed[0])
  39. })
  40. test('nested reactives', () => {
  41. const original = {
  42. nested: {
  43. foo: 1
  44. },
  45. array: [{ bar: 2 }]
  46. }
  47. const observed = reactive(original)
  48. expect(isReactive(observed.nested)).toBe(true)
  49. expect(isReactive(observed.array)).toBe(true)
  50. expect(isReactive(observed.array[0])).toBe(true)
  51. })
  52. test('observed value should proxy mutations to original (Object)', () => {
  53. const original: any = { foo: 1 }
  54. const observed = reactive(original)
  55. // set
  56. observed.bar = 1
  57. expect(observed.bar).toBe(1)
  58. expect(original.bar).toBe(1)
  59. // delete
  60. delete observed.foo
  61. expect('foo' in observed).toBe(false)
  62. expect('foo' in original).toBe(false)
  63. })
  64. test('observed value should proxy mutations to original (Array)', () => {
  65. const original: any[] = [{ foo: 1 }, { bar: 2 }]
  66. const observed = reactive(original)
  67. // set
  68. const value = { baz: 3 }
  69. const reactiveValue = reactive(value)
  70. observed[0] = value
  71. expect(observed[0]).toBe(reactiveValue)
  72. expect(original[0]).toBe(value)
  73. // delete
  74. delete observed[0]
  75. expect(observed[0]).toBeUndefined()
  76. expect(original[0]).toBeUndefined()
  77. // mutating methods
  78. observed.push(value)
  79. expect(observed[2]).toBe(reactiveValue)
  80. expect(original[2]).toBe(value)
  81. })
  82. test('setting a property with an unobserved value should wrap with reactive', () => {
  83. const observed = reactive<{ foo?: object }>({})
  84. const raw = {}
  85. observed.foo = raw
  86. expect(observed.foo).not.toBe(raw)
  87. expect(isReactive(observed.foo)).toBe(true)
  88. })
  89. test('observing already observed value should return same Proxy', () => {
  90. const original = { foo: 1 }
  91. const observed = reactive(original)
  92. const observed2 = reactive(observed)
  93. expect(observed2).toBe(observed)
  94. })
  95. test('observing the same value multiple times should return same Proxy', () => {
  96. const original = { foo: 1 }
  97. const observed = reactive(original)
  98. const observed2 = reactive(original)
  99. expect(observed2).toBe(observed)
  100. })
  101. test('should not pollute original object with Proxies', () => {
  102. const original: any = { foo: 1 }
  103. const original2 = { bar: 2 }
  104. const observed = reactive(original)
  105. const observed2 = reactive(original2)
  106. observed.bar = observed2
  107. expect(observed.bar).toBe(observed2)
  108. expect(original.bar).toBe(original2)
  109. })
  110. test('unwrap', () => {
  111. const original = { foo: 1 }
  112. const observed = reactive(original)
  113. expect(toRaw(observed)).toBe(original)
  114. expect(toRaw(original)).toBe(original)
  115. })
  116. test('non-observable values', () => {
  117. const assertValue = (value: any) => {
  118. reactive(value)
  119. expect(
  120. `value cannot be made reactive: ${String(value)}`
  121. ).toHaveBeenWarnedLast()
  122. }
  123. // number
  124. assertValue(1)
  125. // string
  126. assertValue('foo')
  127. // boolean
  128. assertValue(false)
  129. // null
  130. assertValue(null)
  131. // undefined
  132. assertValue(undefined)
  133. // symbol
  134. const s = Symbol()
  135. assertValue(s)
  136. // built-ins should work and return same value
  137. const p = Promise.resolve()
  138. expect(reactive(p)).toBe(p)
  139. const r = new RegExp('')
  140. expect(reactive(r)).toBe(r)
  141. const d = new Date()
  142. expect(reactive(d)).toBe(d)
  143. })
  144. test('markNonReactive', () => {
  145. const obj = reactive({
  146. foo: { a: 1 },
  147. bar: markNonReactive({ b: 2 })
  148. })
  149. expect(isReactive(obj.foo)).toBe(true)
  150. expect(isReactive(obj.bar)).toBe(false)
  151. })
  152. })