Set.spec.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. import { reactive, effect, isReactive, toRaw } from '../../src'
  2. import { mockWarn } from '@vue/shared'
  3. describe('reactivity/collections', () => {
  4. describe('Set', () => {
  5. mockWarn()
  6. it('instanceof', () => {
  7. const original = new Set()
  8. const observed = reactive(original)
  9. expect(isReactive(observed)).toBe(true)
  10. expect(original instanceof Set).toBe(true)
  11. expect(observed instanceof Set).toBe(true)
  12. })
  13. it('should observe mutations', () => {
  14. let dummy
  15. const set = reactive(new Set())
  16. effect(() => (dummy = set.has('value')))
  17. expect(dummy).toBe(false)
  18. set.add('value')
  19. expect(dummy).toBe(true)
  20. set.delete('value')
  21. expect(dummy).toBe(false)
  22. })
  23. it('should observe mutations with observed value', () => {
  24. let dummy
  25. const value = reactive({})
  26. const set = reactive(new Set())
  27. effect(() => (dummy = set.has(value)))
  28. expect(dummy).toBe(false)
  29. set.add(value)
  30. expect(dummy).toBe(true)
  31. set.delete(value)
  32. expect(dummy).toBe(false)
  33. })
  34. it('should observe for of iteration', () => {
  35. let dummy
  36. const set = reactive(new Set() as Set<number>)
  37. effect(() => {
  38. dummy = 0
  39. for (let num of set) {
  40. dummy += num
  41. }
  42. })
  43. expect(dummy).toBe(0)
  44. set.add(2)
  45. set.add(1)
  46. expect(dummy).toBe(3)
  47. set.delete(2)
  48. expect(dummy).toBe(1)
  49. set.clear()
  50. expect(dummy).toBe(0)
  51. })
  52. it('should observe forEach iteration', () => {
  53. let dummy: any
  54. const set = reactive(new Set())
  55. effect(() => {
  56. dummy = 0
  57. set.forEach(num => (dummy += num))
  58. })
  59. expect(dummy).toBe(0)
  60. set.add(2)
  61. set.add(1)
  62. expect(dummy).toBe(3)
  63. set.delete(2)
  64. expect(dummy).toBe(1)
  65. set.clear()
  66. expect(dummy).toBe(0)
  67. })
  68. it('should observe values iteration', () => {
  69. let dummy
  70. const set = reactive(new Set() as Set<number>)
  71. effect(() => {
  72. dummy = 0
  73. for (let num of set.values()) {
  74. dummy += num
  75. }
  76. })
  77. expect(dummy).toBe(0)
  78. set.add(2)
  79. set.add(1)
  80. expect(dummy).toBe(3)
  81. set.delete(2)
  82. expect(dummy).toBe(1)
  83. set.clear()
  84. expect(dummy).toBe(0)
  85. })
  86. it('should observe keys iteration', () => {
  87. let dummy
  88. const set = reactive(new Set() as Set<number>)
  89. effect(() => {
  90. dummy = 0
  91. for (let num of set.keys()) {
  92. dummy += num
  93. }
  94. })
  95. expect(dummy).toBe(0)
  96. set.add(2)
  97. set.add(1)
  98. expect(dummy).toBe(3)
  99. set.delete(2)
  100. expect(dummy).toBe(1)
  101. set.clear()
  102. expect(dummy).toBe(0)
  103. })
  104. it('should observe entries iteration', () => {
  105. let dummy
  106. const set = reactive(new Set<number>())
  107. effect(() => {
  108. dummy = 0
  109. // eslint-disable-next-line no-unused-vars
  110. for (let [key, num] of set.entries()) {
  111. key
  112. dummy += num
  113. }
  114. })
  115. expect(dummy).toBe(0)
  116. set.add(2)
  117. set.add(1)
  118. expect(dummy).toBe(3)
  119. set.delete(2)
  120. expect(dummy).toBe(1)
  121. set.clear()
  122. expect(dummy).toBe(0)
  123. })
  124. it('should be triggered by clearing', () => {
  125. let dummy
  126. const set = reactive(new Set())
  127. effect(() => (dummy = set.has('key')))
  128. expect(dummy).toBe(false)
  129. set.add('key')
  130. expect(dummy).toBe(true)
  131. set.clear()
  132. expect(dummy).toBe(false)
  133. })
  134. it('should not observe custom property mutations', () => {
  135. let dummy
  136. const set: any = reactive(new Set())
  137. effect(() => (dummy = set.customProp))
  138. expect(dummy).toBe(undefined)
  139. set.customProp = 'Hello World'
  140. expect(dummy).toBe(undefined)
  141. })
  142. it('should observe size mutations', () => {
  143. let dummy
  144. const set = reactive(new Set())
  145. effect(() => (dummy = set.size))
  146. expect(dummy).toBe(0)
  147. set.add('value')
  148. set.add('value2')
  149. expect(dummy).toBe(2)
  150. set.delete('value')
  151. expect(dummy).toBe(1)
  152. set.clear()
  153. expect(dummy).toBe(0)
  154. })
  155. it('should not observe non value changing mutations', () => {
  156. let dummy
  157. const set = reactive(new Set())
  158. const setSpy = jest.fn(() => (dummy = set.has('value')))
  159. effect(setSpy)
  160. expect(dummy).toBe(false)
  161. expect(setSpy).toHaveBeenCalledTimes(1)
  162. set.add('value')
  163. expect(dummy).toBe(true)
  164. expect(setSpy).toHaveBeenCalledTimes(2)
  165. set.add('value')
  166. expect(dummy).toBe(true)
  167. expect(setSpy).toHaveBeenCalledTimes(2)
  168. set.delete('value')
  169. expect(dummy).toBe(false)
  170. expect(setSpy).toHaveBeenCalledTimes(3)
  171. set.delete('value')
  172. expect(dummy).toBe(false)
  173. expect(setSpy).toHaveBeenCalledTimes(3)
  174. set.clear()
  175. expect(dummy).toBe(false)
  176. expect(setSpy).toHaveBeenCalledTimes(3)
  177. })
  178. it('should not observe raw data', () => {
  179. let dummy
  180. const set = reactive(new Set())
  181. effect(() => (dummy = toRaw(set).has('value')))
  182. expect(dummy).toBe(false)
  183. set.add('value')
  184. expect(dummy).toBe(false)
  185. })
  186. it('should not observe raw iterations', () => {
  187. let dummy = 0
  188. const set = reactive(new Set<number>())
  189. effect(() => {
  190. dummy = 0
  191. for (let [num] of toRaw(set).entries()) {
  192. dummy += num
  193. }
  194. for (let num of toRaw(set).keys()) {
  195. dummy += num
  196. }
  197. for (let num of toRaw(set).values()) {
  198. dummy += num
  199. }
  200. toRaw(set).forEach(num => {
  201. dummy += num
  202. })
  203. for (let num of toRaw(set)) {
  204. dummy += num
  205. }
  206. })
  207. expect(dummy).toBe(0)
  208. set.add(2)
  209. set.add(3)
  210. expect(dummy).toBe(0)
  211. set.delete(2)
  212. expect(dummy).toBe(0)
  213. })
  214. it('should not be triggered by raw mutations', () => {
  215. let dummy
  216. const set = reactive(new Set())
  217. effect(() => (dummy = set.has('value')))
  218. expect(dummy).toBe(false)
  219. toRaw(set).add('value')
  220. expect(dummy).toBe(false)
  221. dummy = true
  222. toRaw(set).delete('value')
  223. expect(dummy).toBe(true)
  224. toRaw(set).clear()
  225. expect(dummy).toBe(true)
  226. })
  227. it('should not observe raw size mutations', () => {
  228. let dummy
  229. const set = reactive(new Set())
  230. effect(() => (dummy = toRaw(set).size))
  231. expect(dummy).toBe(0)
  232. set.add('value')
  233. expect(dummy).toBe(0)
  234. })
  235. it('should not be triggered by raw size mutations', () => {
  236. let dummy
  237. const set = reactive(new Set())
  238. effect(() => (dummy = set.size))
  239. expect(dummy).toBe(0)
  240. toRaw(set).add('value')
  241. expect(dummy).toBe(0)
  242. })
  243. it('should support objects as key', () => {
  244. let dummy
  245. const key = {}
  246. const set = reactive(new Set())
  247. const setSpy = jest.fn(() => (dummy = set.has(key)))
  248. effect(setSpy)
  249. expect(dummy).toBe(false)
  250. expect(setSpy).toHaveBeenCalledTimes(1)
  251. set.add({})
  252. expect(dummy).toBe(false)
  253. expect(setSpy).toHaveBeenCalledTimes(1)
  254. set.add(key)
  255. expect(dummy).toBe(true)
  256. expect(setSpy).toHaveBeenCalledTimes(2)
  257. })
  258. it('should not pollute original Set with Proxies', () => {
  259. const set = new Set()
  260. const observed = reactive(set)
  261. const value = reactive({})
  262. observed.add(value)
  263. expect(observed.has(value)).toBe(true)
  264. expect(set.has(value)).toBe(false)
  265. })
  266. it('should observe nested values in iterations (forEach)', () => {
  267. const set = reactive(new Set([{ foo: 1 }]))
  268. let dummy: any
  269. effect(() => {
  270. dummy = 0
  271. set.forEach(value => {
  272. expect(isReactive(value)).toBe(true)
  273. dummy += value.foo
  274. })
  275. })
  276. expect(dummy).toBe(1)
  277. set.forEach(value => {
  278. value.foo++
  279. })
  280. expect(dummy).toBe(2)
  281. })
  282. it('should observe nested values in iterations (values)', () => {
  283. const set = reactive(new Set([{ foo: 1 }]))
  284. let dummy: any
  285. effect(() => {
  286. dummy = 0
  287. for (const value of set.values()) {
  288. expect(isReactive(value)).toBe(true)
  289. dummy += value.foo
  290. }
  291. })
  292. expect(dummy).toBe(1)
  293. set.forEach(value => {
  294. value.foo++
  295. })
  296. expect(dummy).toBe(2)
  297. })
  298. it('should observe nested values in iterations (entries)', () => {
  299. const set = reactive(new Set([{ foo: 1 }]))
  300. let dummy: any
  301. effect(() => {
  302. dummy = 0
  303. for (const [key, value] of set.entries()) {
  304. expect(isReactive(key)).toBe(true)
  305. expect(isReactive(value)).toBe(true)
  306. dummy += value.foo
  307. }
  308. })
  309. expect(dummy).toBe(1)
  310. set.forEach(value => {
  311. value.foo++
  312. })
  313. expect(dummy).toBe(2)
  314. })
  315. it('should observe nested values in iterations (for...of)', () => {
  316. const set = reactive(new Set([{ foo: 1 }]))
  317. let dummy: any
  318. effect(() => {
  319. dummy = 0
  320. for (const value of set) {
  321. expect(isReactive(value)).toBe(true)
  322. dummy += value.foo
  323. }
  324. })
  325. expect(dummy).toBe(1)
  326. set.forEach(value => {
  327. value.foo++
  328. })
  329. expect(dummy).toBe(2)
  330. })
  331. it('should work with reactive entries in raw set', () => {
  332. const raw = new Set()
  333. const entry = reactive({})
  334. raw.add(entry)
  335. const set = reactive(raw)
  336. expect(set.has(entry)).toBe(true)
  337. expect(set.delete(entry)).toBe(true)
  338. expect(set.has(entry)).toBe(false)
  339. })
  340. it('should track deletion of reactive entries in raw set', () => {
  341. const raw = new Set()
  342. const entry = reactive({})
  343. raw.add(entry)
  344. const set = reactive(raw)
  345. let dummy
  346. effect(() => {
  347. dummy = set.has(entry)
  348. })
  349. expect(dummy).toBe(true)
  350. set.delete(entry)
  351. expect(dummy).toBe(false)
  352. })
  353. it('should warn when set contains both raw and reactive versions of the same object', () => {
  354. const raw = new Set()
  355. const rawKey = {}
  356. const key = reactive(rawKey)
  357. raw.add(rawKey)
  358. raw.add(key)
  359. const set = reactive(raw)
  360. set.delete(key)
  361. expect(
  362. `Reactive Set contains both the raw and reactive`
  363. ).toHaveBeenWarned()
  364. })
  365. })
  366. })