reactive.spec.ts 5.4 KB

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