reactiveArray.spec.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. import { type ComputedRef, computed } from '../src/computed'
  2. import { isReactive, reactive, shallowReactive, toRaw } from '../src/reactive'
  3. import { isRef, ref } from '../src/ref'
  4. import { effect } from '../src/effect'
  5. describe('reactivity/reactive/Array', () => {
  6. test('should make Array reactive', () => {
  7. const original = [{ foo: 1 }]
  8. const observed = reactive(original)
  9. expect(observed).not.toBe(original)
  10. expect(isReactive(observed)).toBe(true)
  11. expect(isReactive(original)).toBe(false)
  12. expect(isReactive(observed[0])).toBe(true)
  13. // get
  14. expect(observed[0].foo).toBe(1)
  15. // has
  16. expect(0 in observed).toBe(true)
  17. // ownKeys
  18. expect(Object.keys(observed)).toEqual(['0'])
  19. })
  20. test('cloned reactive Array should point to observed values', () => {
  21. const original = [{ foo: 1 }]
  22. const observed = reactive(original)
  23. const clone = observed.slice()
  24. expect(isReactive(clone[0])).toBe(true)
  25. expect(clone[0]).not.toBe(original[0])
  26. expect(clone[0]).toBe(observed[0])
  27. })
  28. test('observed value should proxy mutations to original (Array)', () => {
  29. const original: any[] = [{ foo: 1 }, { bar: 2 }]
  30. const observed = reactive(original)
  31. // set
  32. const value = { baz: 3 }
  33. const reactiveValue = reactive(value)
  34. observed[0] = value
  35. expect(observed[0]).toBe(reactiveValue)
  36. expect(original[0]).toBe(value)
  37. // delete
  38. delete observed[0]
  39. expect(observed[0]).toBeUndefined()
  40. expect(original[0]).toBeUndefined()
  41. // mutating methods
  42. observed.push(value)
  43. expect(observed[2]).toBe(reactiveValue)
  44. expect(original[2]).toBe(value)
  45. })
  46. test('Array identity methods should work with raw values', () => {
  47. const raw = {}
  48. const arr = reactive([{}, {}])
  49. arr.push(raw)
  50. expect(arr.indexOf(raw)).toBe(2)
  51. expect(arr.indexOf(raw, 3)).toBe(-1)
  52. expect(arr.includes(raw)).toBe(true)
  53. expect(arr.includes(raw, 3)).toBe(false)
  54. expect(arr.lastIndexOf(raw)).toBe(2)
  55. expect(arr.lastIndexOf(raw, 1)).toBe(-1)
  56. // should work also for the observed version
  57. const observed = arr[2]
  58. expect(arr.indexOf(observed)).toBe(2)
  59. expect(arr.indexOf(observed, 3)).toBe(-1)
  60. expect(arr.includes(observed)).toBe(true)
  61. expect(arr.includes(observed, 3)).toBe(false)
  62. expect(arr.lastIndexOf(observed)).toBe(2)
  63. expect(arr.lastIndexOf(observed, 1)).toBe(-1)
  64. })
  65. test('Array identity methods should work if raw value contains reactive objects', () => {
  66. const raw = []
  67. const obj = reactive({})
  68. raw.push(obj)
  69. const arr = reactive(raw)
  70. expect(arr.includes(obj)).toBe(true)
  71. })
  72. test('Array identity methods should be reactive', () => {
  73. const obj = {}
  74. const arr = reactive([obj, {}])
  75. let index: number = -1
  76. effect(() => {
  77. index = arr.indexOf(obj)
  78. })
  79. expect(index).toBe(0)
  80. arr.reverse()
  81. expect(index).toBe(1)
  82. })
  83. test('delete on Array should not trigger length dependency', () => {
  84. const arr = reactive([1, 2, 3])
  85. const fn = vi.fn()
  86. effect(() => {
  87. fn(arr.length)
  88. })
  89. expect(fn).toHaveBeenCalledTimes(1)
  90. delete arr[1]
  91. expect(fn).toHaveBeenCalledTimes(1)
  92. })
  93. test('should track hasOwnProperty call with index', () => {
  94. const original = [1, 2, 3]
  95. const observed = reactive(original)
  96. let dummy
  97. effect(() => {
  98. dummy = observed.hasOwnProperty(0)
  99. })
  100. expect(dummy).toBe(true)
  101. delete observed[0]
  102. expect(dummy).toBe(false)
  103. })
  104. test('shift on Array should trigger dependency once', () => {
  105. const arr = reactive([1, 2, 3])
  106. const fn = vi.fn()
  107. effect(() => {
  108. for (let i = 0; i < arr.length; i++) {
  109. arr[i]
  110. }
  111. fn()
  112. })
  113. expect(fn).toHaveBeenCalledTimes(1)
  114. arr.shift()
  115. expect(fn).toHaveBeenCalledTimes(2)
  116. })
  117. //#6018
  118. test('edge case: avoid trigger effect in deleteProperty when array length-decrease mutation methods called', () => {
  119. const arr = ref([1])
  120. const fn1 = vi.fn()
  121. const fn2 = vi.fn()
  122. effect(() => {
  123. fn1()
  124. if (arr.value.length > 0) {
  125. arr.value.slice()
  126. fn2()
  127. }
  128. })
  129. expect(fn1).toHaveBeenCalledTimes(1)
  130. expect(fn2).toHaveBeenCalledTimes(1)
  131. arr.value.splice(0)
  132. expect(fn1).toHaveBeenCalledTimes(2)
  133. expect(fn2).toHaveBeenCalledTimes(1)
  134. })
  135. test('add existing index on Array should not trigger length dependency', () => {
  136. const array = new Array(3)
  137. const observed = reactive(array)
  138. const fn = vi.fn()
  139. effect(() => {
  140. fn(observed.length)
  141. })
  142. expect(fn).toHaveBeenCalledTimes(1)
  143. observed[1] = 1
  144. expect(fn).toHaveBeenCalledTimes(1)
  145. })
  146. test('add non-integer prop on Array should not trigger length dependency', () => {
  147. const array: any[] & { x?: string } = new Array(3)
  148. const observed = reactive(array)
  149. const fn = vi.fn()
  150. effect(() => {
  151. fn(observed.length)
  152. })
  153. expect(fn).toHaveBeenCalledTimes(1)
  154. observed.x = 'x'
  155. expect(fn).toHaveBeenCalledTimes(1)
  156. observed[-1] = 'x'
  157. expect(fn).toHaveBeenCalledTimes(1)
  158. observed[NaN] = 'x'
  159. expect(fn).toHaveBeenCalledTimes(1)
  160. })
  161. // #2427
  162. test('track length on for ... in iteration', () => {
  163. const array = reactive([1])
  164. let length = ''
  165. effect(() => {
  166. length = ''
  167. for (const key in array) {
  168. length += key
  169. }
  170. })
  171. expect(length).toBe('0')
  172. array.push(1)
  173. expect(length).toBe('01')
  174. })
  175. // #9742
  176. test('mutation on user proxy of reactive Array', () => {
  177. const array = reactive<number[]>([])
  178. const proxy = new Proxy(array, {})
  179. proxy.push(1)
  180. expect(array).toHaveLength(1)
  181. expect(proxy).toHaveLength(1)
  182. })
  183. describe('Array methods w/ refs', () => {
  184. let original: any[]
  185. beforeEach(() => {
  186. original = reactive([1, ref(2)])
  187. })
  188. // read + copy
  189. test('read only copy methods', () => {
  190. const raw = original.concat([3, ref(4)])
  191. expect(isRef(raw[1])).toBe(true)
  192. expect(isRef(raw[3])).toBe(true)
  193. })
  194. // read + write
  195. test('read + write mutating methods', () => {
  196. const res = original.copyWithin(0, 1, 2)
  197. const raw = toRaw(res)
  198. expect(isRef(raw[0])).toBe(true)
  199. expect(isRef(raw[1])).toBe(true)
  200. })
  201. test('read + identity', () => {
  202. const ref = original[1]
  203. expect(ref).toBe(toRaw(original)[1])
  204. expect(original.indexOf(ref)).toBe(1)
  205. })
  206. })
  207. describe('Array subclasses', () => {
  208. class SubArray<T> extends Array<T> {
  209. lastPushed: undefined | T
  210. lastSearched: undefined | T
  211. push(item: T) {
  212. this.lastPushed = item
  213. return super.push(item)
  214. }
  215. indexOf(searchElement: T, fromIndex?: number | undefined): number {
  216. this.lastSearched = searchElement
  217. return super.indexOf(searchElement, fromIndex)
  218. }
  219. }
  220. test('calls correct mutation method on Array subclass', () => {
  221. const subArray = new SubArray(4, 5, 6)
  222. const observed = reactive(subArray)
  223. subArray.push(7)
  224. expect(subArray.lastPushed).toBe(7)
  225. observed.push(9)
  226. expect(observed.lastPushed).toBe(9)
  227. })
  228. test('calls correct identity-sensitive method on Array subclass', () => {
  229. const subArray = new SubArray(4, 5, 6)
  230. const observed = reactive(subArray)
  231. let index
  232. index = subArray.indexOf(4)
  233. expect(index).toBe(0)
  234. expect(subArray.lastSearched).toBe(4)
  235. index = observed.indexOf(6)
  236. expect(index).toBe(2)
  237. expect(observed.lastSearched).toBe(6)
  238. })
  239. })
  240. describe('Optimized array methods:', () => {
  241. test('iterator', () => {
  242. const shallow = shallowReactive([1, 2, 3, 4])
  243. let result = computed(() => {
  244. let sum = 0
  245. for (let x of shallow) {
  246. sum += x ** 2
  247. }
  248. return sum
  249. })
  250. expect(result.value).toBe(30)
  251. shallow[2] = 0
  252. expect(result.value).toBe(21)
  253. const deep = reactive([{ val: 1 }, { val: 2 }])
  254. result = computed(() => {
  255. let sum = 0
  256. for (let x of deep) {
  257. sum += x.val ** 2
  258. }
  259. return sum
  260. })
  261. expect(result.value).toBe(5)
  262. deep[1].val = 3
  263. expect(result.value).toBe(10)
  264. })
  265. test('concat', () => {
  266. const a1 = shallowReactive([1, { val: 2 }])
  267. const a2 = reactive([{ val: 3 }])
  268. const a3 = [4, 5]
  269. let result = computed(() => a1.concat(a2, a3, 6, { val: 7 }))
  270. expect(result.value).toStrictEqual([
  271. 1,
  272. { val: 2 },
  273. { val: 3 },
  274. 4,
  275. 5,
  276. 6,
  277. { val: 7 },
  278. ])
  279. expect(isReactive(result.value[1])).toBe(false)
  280. expect(isReactive(result.value[2])).toBe(true)
  281. expect(isReactive(result.value[6])).toBe(false)
  282. a1.shift()
  283. expect(result.value).toStrictEqual([
  284. { val: 2 },
  285. { val: 3 },
  286. 4,
  287. 5,
  288. 6,
  289. { val: 7 },
  290. ])
  291. a2.pop()
  292. expect(result.value).toStrictEqual([{ val: 2 }, 4, 5, 6, { val: 7 }])
  293. a3.pop()
  294. expect(result.value).toStrictEqual([{ val: 2 }, 4, 5, 6, { val: 7 }])
  295. })
  296. test('entries', () => {
  297. const shallow = shallowReactive([0, 1])
  298. const result1 = computed(() => Array.from(shallow.entries()))
  299. expect(result1.value).toStrictEqual([
  300. [0, 0],
  301. [1, 1],
  302. ])
  303. shallow[1] = 10
  304. expect(result1.value).toStrictEqual([
  305. [0, 0],
  306. [1, 10],
  307. ])
  308. const deep = reactive([{ val: 0 }, { val: 1 }])
  309. const result2 = computed(() => Array.from(deep.entries()))
  310. expect(result2.value).toStrictEqual([
  311. [0, { val: 0 }],
  312. [1, { val: 1 }],
  313. ])
  314. expect(isReactive(result2.value[0][1])).toBe(true)
  315. deep.pop()
  316. expect(Array.from(result2.value)).toStrictEqual([[0, { val: 0 }]])
  317. })
  318. test('every', () => {
  319. const shallow = shallowReactive([1, 2, 5])
  320. let result = computed(() => shallow.every(x => x < 5))
  321. expect(result.value).toBe(false)
  322. shallow.pop()
  323. expect(result.value).toBe(true)
  324. const deep = reactive([{ val: 1 }, { val: 5 }])
  325. result = computed(() => deep.every(x => x.val < 5))
  326. expect(result.value).toBe(false)
  327. deep[1].val = 2
  328. expect(result.value).toBe(true)
  329. })
  330. test('filter', () => {
  331. const shallow = shallowReactive([1, 2, 3, 4])
  332. const result1 = computed(() => shallow.filter(x => x < 3))
  333. expect(result1.value).toStrictEqual([1, 2])
  334. shallow[2] = 0
  335. expect(result1.value).toStrictEqual([1, 2, 0])
  336. const deep = reactive([{ val: 1 }, { val: 2 }])
  337. const result2 = computed(() => deep.filter(x => x.val < 2))
  338. expect(result2.value).toStrictEqual([{ val: 1 }])
  339. expect(isReactive(result2.value[0])).toBe(true)
  340. deep[1].val = 0
  341. expect(result2.value).toStrictEqual([{ val: 1 }, { val: 0 }])
  342. })
  343. test('find and co.', () => {
  344. const shallow = shallowReactive([{ val: 1 }, { val: 2 }])
  345. let find = computed(() => shallow.find(x => x.val === 2))
  346. // @ts-expect-error tests are not limited to es2016
  347. let findLast = computed(() => shallow.findLast(x => x.val === 2))
  348. let findIndex = computed(() => shallow.findIndex(x => x.val === 2))
  349. let findLastIndex = computed(() =>
  350. // @ts-expect-error tests are not limited to es2016
  351. shallow.findLastIndex(x => x.val === 2),
  352. )
  353. expect(find.value).toBe(shallow[1])
  354. expect(isReactive(find.value)).toBe(false)
  355. expect(findLast.value).toBe(shallow[1])
  356. expect(isReactive(findLast.value)).toBe(false)
  357. expect(findIndex.value).toBe(1)
  358. expect(findLastIndex.value).toBe(1)
  359. shallow[1].val = 0
  360. expect(find.value).toBe(shallow[1])
  361. expect(findLast.value).toBe(shallow[1])
  362. expect(findIndex.value).toBe(1)
  363. expect(findLastIndex.value).toBe(1)
  364. shallow.pop()
  365. expect(find.value).toBe(undefined)
  366. expect(findLast.value).toBe(undefined)
  367. expect(findIndex.value).toBe(-1)
  368. expect(findLastIndex.value).toBe(-1)
  369. const deep = reactive([{ val: 1 }, { val: 2 }])
  370. find = computed(() => deep.find(x => x.val === 2))
  371. // @ts-expect-error tests are not limited to es2016
  372. findLast = computed(() => deep.findLast(x => x.val === 2))
  373. findIndex = computed(() => deep.findIndex(x => x.val === 2))
  374. // @ts-expect-error tests are not limited to es2016
  375. findLastIndex = computed(() => deep.findLastIndex(x => x.val === 2))
  376. expect(find.value).toBe(deep[1])
  377. expect(isReactive(find.value)).toBe(true)
  378. expect(findLast.value).toBe(deep[1])
  379. expect(isReactive(findLast.value)).toBe(true)
  380. expect(findIndex.value).toBe(1)
  381. expect(findLastIndex.value).toBe(1)
  382. deep[1].val = 0
  383. expect(find.value).toBe(undefined)
  384. expect(findLast.value).toBe(undefined)
  385. expect(findIndex.value).toBe(-1)
  386. expect(findLastIndex.value).toBe(-1)
  387. })
  388. test('forEach', () => {
  389. const shallow = shallowReactive([1, 2, 3, 4])
  390. let result = computed(() => {
  391. let sum = 0
  392. shallow.forEach(x => (sum += x ** 2))
  393. return sum
  394. })
  395. expect(result.value).toBe(30)
  396. shallow[2] = 0
  397. expect(result.value).toBe(21)
  398. const deep = reactive([{ val: 1 }, { val: 2 }])
  399. result = computed(() => {
  400. let sum = 0
  401. deep.forEach(x => (sum += x.val ** 2))
  402. return sum
  403. })
  404. expect(result.value).toBe(5)
  405. deep[1].val = 3
  406. expect(result.value).toBe(10)
  407. })
  408. test('join', () => {
  409. function toString(this: { val: number }) {
  410. return this.val
  411. }
  412. const shallow = shallowReactive([
  413. { val: 1, toString },
  414. { val: 2, toString },
  415. ])
  416. let result = computed(() => shallow.join('+'))
  417. expect(result.value).toBe('1+2')
  418. shallow[1].val = 23
  419. expect(result.value).toBe('1+2')
  420. shallow.pop()
  421. expect(result.value).toBe('1')
  422. const deep = reactive([
  423. { val: 1, toString },
  424. { val: 2, toString },
  425. ])
  426. result = computed(() => deep.join())
  427. expect(result.value).toBe('1,2')
  428. deep[1].val = 23
  429. expect(result.value).toBe('1,23')
  430. })
  431. test('map', () => {
  432. const shallow = shallowReactive([1, 2, 3, 4])
  433. let result = computed(() => shallow.map(x => x ** 2))
  434. expect(result.value).toStrictEqual([1, 4, 9, 16])
  435. shallow[2] = 0
  436. expect(result.value).toStrictEqual([1, 4, 0, 16])
  437. const deep = reactive([{ val: 1 }, { val: 2 }])
  438. result = computed(() => deep.map(x => x.val ** 2))
  439. expect(result.value).toStrictEqual([1, 4])
  440. deep[1].val = 3
  441. expect(result.value).toStrictEqual([1, 9])
  442. })
  443. test('reduce left and right', () => {
  444. function toString(this: any) {
  445. return this.val + '-'
  446. }
  447. const shallow = shallowReactive([
  448. { val: 1, toString },
  449. { val: 2, toString },
  450. ] as any[])
  451. expect(shallow.reduce((acc, x) => acc + '' + x.val, undefined)).toBe(
  452. 'undefined12',
  453. )
  454. let left = computed(() => shallow.reduce((acc, x) => acc + '' + x.val))
  455. let right = computed(() =>
  456. shallow.reduceRight((acc, x) => acc + '' + x.val),
  457. )
  458. expect(left.value).toBe('1-2')
  459. expect(right.value).toBe('2-1')
  460. shallow[1].val = 23
  461. expect(left.value).toBe('1-2')
  462. expect(right.value).toBe('2-1')
  463. shallow.pop()
  464. expect(left.value).toBe(shallow[0])
  465. expect(right.value).toBe(shallow[0])
  466. const deep = reactive([{ val: 1 }, { val: 2 }])
  467. left = computed(() => deep.reduce((acc, x) => acc + x.val, '0'))
  468. right = computed(() => deep.reduceRight((acc, x) => acc + x.val, '3'))
  469. expect(left.value).toBe('012')
  470. expect(right.value).toBe('321')
  471. deep[1].val = 23
  472. expect(left.value).toBe('0123')
  473. expect(right.value).toBe('3231')
  474. })
  475. test('some', () => {
  476. const shallow = shallowReactive([1, 2, 5])
  477. let result = computed(() => shallow.some(x => x > 4))
  478. expect(result.value).toBe(true)
  479. shallow.pop()
  480. expect(result.value).toBe(false)
  481. const deep = reactive([{ val: 1 }, { val: 5 }])
  482. result = computed(() => deep.some(x => x.val > 4))
  483. expect(result.value).toBe(true)
  484. deep[1].val = 2
  485. expect(result.value).toBe(false)
  486. })
  487. // Node 20+
  488. // @ts-expect-error tests are not limited to es2016
  489. test.skipIf(!Array.prototype.toReversed)('toReversed', () => {
  490. const array = reactive([1, { val: 2 }])
  491. const result = computed(() => (array as any).toReversed())
  492. expect(result.value).toStrictEqual([{ val: 2 }, 1])
  493. expect(isReactive(result.value[0])).toBe(true)
  494. array.splice(1, 1, 2)
  495. expect(result.value).toStrictEqual([2, 1])
  496. })
  497. // Node 20+
  498. // @ts-expect-error tests are not limited to es2016
  499. test.skipIf(!Array.prototype.toSorted)('toSorted', () => {
  500. // No comparer
  501. // @ts-expect-error
  502. expect(shallowReactive([2, 1, 3]).toSorted()).toStrictEqual([1, 2, 3])
  503. const shallow = shallowReactive([{ val: 2 }, { val: 1 }, { val: 3 }])
  504. let result: ComputedRef<{ val: number }[]>
  505. // @ts-expect-error
  506. result = computed(() => shallow.toSorted((a, b) => a.val - b.val))
  507. expect(result.value.map(x => x.val)).toStrictEqual([1, 2, 3])
  508. expect(isReactive(result.value[0])).toBe(false)
  509. shallow[0].val = 4
  510. expect(result.value.map(x => x.val)).toStrictEqual([1, 4, 3])
  511. shallow.pop()
  512. expect(result.value.map(x => x.val)).toStrictEqual([1, 4])
  513. const deep = reactive([{ val: 2 }, { val: 1 }, { val: 3 }])
  514. // @ts-expect-error
  515. result = computed(() => deep.toSorted((a, b) => a.val - b.val))
  516. expect(result.value.map(x => x.val)).toStrictEqual([1, 2, 3])
  517. expect(isReactive(result.value[0])).toBe(true)
  518. deep[0].val = 4
  519. expect(result.value.map(x => x.val)).toStrictEqual([1, 3, 4])
  520. })
  521. // Node 20+
  522. // @ts-expect-error tests are not limited to es2016
  523. test.skipIf(!Array.prototype.toSpliced)('toSpliced', () => {
  524. const array = reactive([1, 2, 3])
  525. // @ts-expect-error
  526. const result = computed(() => array.toSpliced(1, 1, -2))
  527. expect(result.value).toStrictEqual([1, -2, 3])
  528. array[0] = 0
  529. expect(result.value).toStrictEqual([0, -2, 3])
  530. })
  531. test('values', () => {
  532. const shallow = shallowReactive([{ val: 1 }, { val: 2 }])
  533. const result = computed(() => Array.from(shallow.values()))
  534. expect(result.value).toStrictEqual([{ val: 1 }, { val: 2 }])
  535. expect(isReactive(result.value[0])).toBe(false)
  536. shallow.pop()
  537. expect(result.value).toStrictEqual([{ val: 1 }])
  538. const deep = reactive([{ val: 1 }, { val: 2 }])
  539. const firstItem = Array.from(deep.values())[0]
  540. expect(isReactive(firstItem)).toBe(true)
  541. })
  542. test('extend methods', () => {
  543. class Collection extends Array {
  544. // @ts-expect-error
  545. every(foo: any, bar: any, baz: any) {
  546. expect(foo).toBe('foo')
  547. expect(bar).toBe('bar')
  548. expect(baz).toBe('baz')
  549. return super.every(obj => obj.id === foo)
  550. }
  551. // @ts-expect-error
  552. filter(foo: any, bar: any, baz: any) {
  553. expect(foo).toBe('foo')
  554. expect(bar).toBe('bar')
  555. expect(baz).toBe('baz')
  556. return super.filter(obj => obj.id === foo)
  557. }
  558. // @ts-expect-error
  559. find(foo: any, bar: any, baz: any) {
  560. expect(foo).toBe('foo')
  561. expect(bar).toBe('bar')
  562. expect(baz).toBe('baz')
  563. return super.find(obj => obj.id === foo)
  564. }
  565. // @ts-expect-error
  566. findIndex(foo: any, bar: any, baz: any) {
  567. expect(foo).toBe('foo')
  568. expect(bar).toBe('bar')
  569. expect(baz).toBe('baz')
  570. return super.findIndex(obj => obj.id === bar)
  571. }
  572. findLast(foo: any, bar: any, baz: any) {
  573. expect(foo).toBe('foo')
  574. expect(bar).toBe('bar')
  575. expect(baz).toBe('baz')
  576. // @ts-expect-error our code is limited to es2016 but user code is not
  577. return super.findLast(obj => obj.id === bar)
  578. }
  579. findLastIndex(foo: any, bar: any, baz: any) {
  580. expect(foo).toBe('foo')
  581. expect(bar).toBe('bar')
  582. expect(baz).toBe('baz')
  583. return super.findIndex(obj => obj.id === bar)
  584. }
  585. // @ts-expect-error
  586. forEach(foo: any, bar: any, baz: any) {
  587. expect(foo).toBe('foo')
  588. expect(bar).toBe('bar')
  589. expect(baz).toBe('baz')
  590. }
  591. // @ts-expect-error
  592. map(foo: any, bar: any, baz: any) {
  593. expect(foo).toBe('foo')
  594. expect(bar).toBe('bar')
  595. expect(baz).toBe('baz')
  596. return super.map(obj => obj.value)
  597. }
  598. // @ts-expect-error
  599. some(foo: any, bar: any, baz: any) {
  600. expect(foo).toBe('foo')
  601. expect(bar).toBe('bar')
  602. expect(baz).toBe('baz')
  603. return super.some(obj => obj.id === baz)
  604. }
  605. }
  606. const state = reactive({
  607. things: new Collection(),
  608. })
  609. const foo = { id: 'foo', value: '1' }
  610. const bar = { id: 'bar', value: '2' }
  611. const baz = { id: 'baz', value: '3' }
  612. state.things.push(foo)
  613. state.things.push(bar)
  614. state.things.push(baz)
  615. expect(state.things.every('foo', 'bar', 'baz')).toBe(false)
  616. expect(state.things.filter('foo', 'bar', 'baz')).toEqual([foo])
  617. const _foo = state.things.find('foo', 'bar', 'baz')
  618. expect(isReactive(_foo)).toBe(true)
  619. expect(foo).toStrictEqual(_foo)
  620. expect(state.things.findIndex('foo', 'bar', 'baz')).toBe(1)
  621. const _bar = state.things.findLast('foo', 'bar', 'baz')
  622. expect(isReactive(_bar)).toBe(true)
  623. expect(bar).toStrictEqual(_bar)
  624. expect(state.things.findLastIndex('foo', 'bar', 'baz')).toBe(1)
  625. expect(state.things.forEach('foo', 'bar', 'baz')).toBeUndefined()
  626. expect(state.things.map('foo', 'bar', 'baz')).toEqual(['1', '2', '3'])
  627. expect(state.things.some('foo', 'bar', 'baz')).toBe(true)
  628. {
  629. class Collection extends Array {
  630. find(matcher: any) {
  631. return super.find(matcher)
  632. }
  633. }
  634. const state = reactive({
  635. // @ts-expect-error
  636. things: new Collection({ foo: '' }),
  637. })
  638. const bar = computed(() => {
  639. return state.things.find((obj: any) => obj.foo === 'bar')
  640. })
  641. bar.value
  642. state.things[0].foo = 'bar'
  643. expect(bar.value).toEqual({ foo: 'bar' })
  644. }
  645. })
  646. })
  647. })