reactive.spec.ts 4.6 KB

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