Set.spec.ts 12 KB

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