@esportsplus/reactivity 0.30.2 → 0.30.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/reactive/array.js +24 -22
- package/build/reactive/object.js +3 -2
- package/build/system.js +4 -3
- package/package.json +3 -2
- package/src/reactive/array.ts +27 -26
- package/src/reactive/object.ts +5 -2
- package/src/system.ts +5 -3
- package/tests/array.ts +657 -0
- package/tests/bench/array.ts +162 -0
- package/tests/bench/system.ts +230 -0
- package/tests/effects.ts +211 -0
- package/tests/nested.ts +293 -0
- package/tests/objects.ts +185 -0
- package/tests/primitives.ts +280 -0
- package/tests/reactive.ts +324 -0
- package/tests/system.ts +655 -0
- package/vitest.config.ts +18 -0
- package/test/arrays.ts +0 -146
- package/test/debug.ts +0 -7
- package/test/effects.ts +0 -168
- package/test/index.ts +0 -8
- package/test/nested.ts +0 -201
- package/test/objects.ts +0 -106
- package/test/primitives.ts +0 -87
- package/test/range.ts +0 -45
- package/test/vite.config.ts +0 -41
package/tests/array.ts
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { effect, read, signal, write } from '~/system';
|
|
3
|
+
import { ReactiveArray } from '~/reactive/array';
|
|
4
|
+
import reactive from '~/reactive/index';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
describe('ReactiveArray', () => {
|
|
8
|
+
describe('constructor', () => {
|
|
9
|
+
it('creates empty array', () => {
|
|
10
|
+
let arr = new ReactiveArray<number>();
|
|
11
|
+
|
|
12
|
+
expect(arr.length).toBe(0);
|
|
13
|
+
expect(arr.$length).toBe(0);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('creates array with initial items', () => {
|
|
17
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
18
|
+
|
|
19
|
+
expect(arr.length).toBe(3);
|
|
20
|
+
expect(arr[0]).toBe(1);
|
|
21
|
+
expect(arr[1]).toBe(2);
|
|
22
|
+
expect(arr[2]).toBe(3);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('$length is reactive', async () => {
|
|
26
|
+
let arr = new ReactiveArray(1, 2, 3),
|
|
27
|
+
lengths: number[] = [];
|
|
28
|
+
|
|
29
|
+
effect(() => {
|
|
30
|
+
lengths.push(arr.$length);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(lengths).toEqual([3]);
|
|
34
|
+
|
|
35
|
+
arr.push(4);
|
|
36
|
+
await Promise.resolve();
|
|
37
|
+
|
|
38
|
+
expect(lengths).toEqual([3, 4]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
describe('$set', () => {
|
|
44
|
+
it('sets value at index', () => {
|
|
45
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
46
|
+
|
|
47
|
+
arr.$set(1, 20);
|
|
48
|
+
|
|
49
|
+
expect(arr[1]).toBe(20);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('skips if same value', () => {
|
|
53
|
+
let arr = new ReactiveArray(1, 2, 3),
|
|
54
|
+
dispatched = false;
|
|
55
|
+
|
|
56
|
+
arr.on('set', () => { dispatched = true; });
|
|
57
|
+
arr.$set(0, 1);
|
|
58
|
+
|
|
59
|
+
expect(dispatched).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('dispatches set event', () => {
|
|
63
|
+
let arr = new ReactiveArray(1, 2, 3),
|
|
64
|
+
events: { index: number; item: number }[] = [];
|
|
65
|
+
|
|
66
|
+
arr.on('set', (e) => { events.push(e); });
|
|
67
|
+
arr.$set(0, 10);
|
|
68
|
+
|
|
69
|
+
expect(events).toEqual([{ index: 0, item: 10 }]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('updates length when setting beyond current length', async () => {
|
|
73
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
74
|
+
|
|
75
|
+
arr.$set(5, 99);
|
|
76
|
+
|
|
77
|
+
expect(arr[5]).toBe(99);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
describe('$length', () => {
|
|
83
|
+
it('getter returns reactive length', () => {
|
|
84
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
85
|
+
|
|
86
|
+
expect(arr.$length).toBe(3);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('setter truncates array via splice', () => {
|
|
90
|
+
let arr = new ReactiveArray(1, 2, 3, 4, 5);
|
|
91
|
+
|
|
92
|
+
arr.$length = 2;
|
|
93
|
+
|
|
94
|
+
expect(arr.length).toBe(2);
|
|
95
|
+
expect(arr[0]).toBe(1);
|
|
96
|
+
expect(arr[1]).toBe(2);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('throws when setting length larger than current', () => {
|
|
100
|
+
let arr = new ReactiveArray(1, 2);
|
|
101
|
+
|
|
102
|
+
expect(() => { arr.$length = 5; }).toThrow();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
describe('push', () => {
|
|
108
|
+
it('adds items', () => {
|
|
109
|
+
let arr = new ReactiveArray<number>();
|
|
110
|
+
|
|
111
|
+
arr.push(1, 2, 3);
|
|
112
|
+
|
|
113
|
+
expect(arr.length).toBe(3);
|
|
114
|
+
expect(arr[0]).toBe(1);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('returns new length', () => {
|
|
118
|
+
let arr = new ReactiveArray(1);
|
|
119
|
+
|
|
120
|
+
expect(arr.push(2, 3)).toBe(3);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('dispatches push event', () => {
|
|
124
|
+
let arr = new ReactiveArray<number>(),
|
|
125
|
+
pushed: number[][] = [];
|
|
126
|
+
|
|
127
|
+
arr.on('push', (e) => { pushed.push(e.items); });
|
|
128
|
+
arr.push(1, 2);
|
|
129
|
+
|
|
130
|
+
expect(pushed).toEqual([[1, 2]]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('no-op for empty push', () => {
|
|
134
|
+
let arr = new ReactiveArray(1),
|
|
135
|
+
dispatched = false;
|
|
136
|
+
|
|
137
|
+
arr.on('push', () => { dispatched = true; });
|
|
138
|
+
arr.push();
|
|
139
|
+
|
|
140
|
+
expect(dispatched).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('updates reactive length', async () => {
|
|
144
|
+
let arr = new ReactiveArray<number>(),
|
|
145
|
+
lengths: number[] = [];
|
|
146
|
+
|
|
147
|
+
effect(() => { lengths.push(arr.$length); });
|
|
148
|
+
arr.push(1);
|
|
149
|
+
await Promise.resolve();
|
|
150
|
+
|
|
151
|
+
expect(lengths).toEqual([0, 1]);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
describe('pop', () => {
|
|
157
|
+
it('removes last item', () => {
|
|
158
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
159
|
+
|
|
160
|
+
expect(arr.pop()).toBe(3);
|
|
161
|
+
expect(arr.length).toBe(2);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('returns undefined for empty array', () => {
|
|
165
|
+
let arr = new ReactiveArray<number>();
|
|
166
|
+
|
|
167
|
+
expect(arr.pop()).toBe(undefined);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('dispatches pop event', () => {
|
|
171
|
+
let arr = new ReactiveArray(1, 2),
|
|
172
|
+
events: { item: number }[] = [];
|
|
173
|
+
|
|
174
|
+
arr.on('pop', (e) => { events.push(e); });
|
|
175
|
+
arr.pop();
|
|
176
|
+
|
|
177
|
+
expect(events).toEqual([{ item: 2 }]);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('does not dispatch for empty array', () => {
|
|
181
|
+
let arr = new ReactiveArray<number>(),
|
|
182
|
+
dispatched = false;
|
|
183
|
+
|
|
184
|
+
arr.on('pop', () => { dispatched = true; });
|
|
185
|
+
arr.pop();
|
|
186
|
+
|
|
187
|
+
expect(dispatched).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
describe('shift', () => {
|
|
193
|
+
it('removes first item', () => {
|
|
194
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
195
|
+
|
|
196
|
+
expect(arr.shift()).toBe(1);
|
|
197
|
+
expect(arr.length).toBe(2);
|
|
198
|
+
expect(arr[0]).toBe(2);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('returns undefined for empty array', () => {
|
|
202
|
+
let arr = new ReactiveArray<number>();
|
|
203
|
+
|
|
204
|
+
expect(arr.shift()).toBe(undefined);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('dispatches shift event', () => {
|
|
208
|
+
let arr = new ReactiveArray(10, 20),
|
|
209
|
+
events: { item: number }[] = [];
|
|
210
|
+
|
|
211
|
+
arr.on('shift', (e) => { events.push(e); });
|
|
212
|
+
arr.shift();
|
|
213
|
+
|
|
214
|
+
expect(events).toEqual([{ item: 10 }]);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
describe('unshift', () => {
|
|
220
|
+
it('adds items to front', () => {
|
|
221
|
+
let arr = new ReactiveArray<number>();
|
|
222
|
+
|
|
223
|
+
arr.push(3);
|
|
224
|
+
arr.unshift(1, 2);
|
|
225
|
+
|
|
226
|
+
expect(arr[0]).toBe(1);
|
|
227
|
+
expect(arr[1]).toBe(2);
|
|
228
|
+
expect(arr[2]).toBe(3);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('returns new length', () => {
|
|
232
|
+
let arr = new ReactiveArray(1);
|
|
233
|
+
|
|
234
|
+
expect(arr.unshift(0)).toBe(2);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('dispatches unshift event', () => {
|
|
238
|
+
let arr = new ReactiveArray<number>(),
|
|
239
|
+
events: number[][] = [];
|
|
240
|
+
|
|
241
|
+
arr.on('unshift', (e) => { events.push(e.items); });
|
|
242
|
+
arr.unshift(1, 2);
|
|
243
|
+
|
|
244
|
+
expect(events).toEqual([[1, 2]]);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
describe('splice', () => {
|
|
250
|
+
it('removes items', () => {
|
|
251
|
+
let arr = new ReactiveArray(1, 2, 3, 4, 5);
|
|
252
|
+
|
|
253
|
+
let removed = arr.splice(1, 2);
|
|
254
|
+
|
|
255
|
+
expect([...removed]).toEqual([2, 3]);
|
|
256
|
+
expect(arr.length).toBe(3);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('inserts items', () => {
|
|
260
|
+
let arr = new ReactiveArray(1, 4, 5);
|
|
261
|
+
|
|
262
|
+
arr.splice(1, 0, 2, 3);
|
|
263
|
+
|
|
264
|
+
expect([...arr]).toEqual([1, 2, 3, 4, 5]);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('replaces items', () => {
|
|
268
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
269
|
+
|
|
270
|
+
arr.splice(1, 1, 20);
|
|
271
|
+
|
|
272
|
+
expect([...arr]).toEqual([1, 20, 3]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('dispatches splice event', () => {
|
|
276
|
+
let arr = new ReactiveArray(1, 2, 3),
|
|
277
|
+
events: { start: number; deleteCount: number; items: number[] }[] = [];
|
|
278
|
+
|
|
279
|
+
arr.on('splice', (e) => { events.push(e); });
|
|
280
|
+
arr.splice(1, 1, 20, 30);
|
|
281
|
+
|
|
282
|
+
expect(events).toEqual([{ start: 1, deleteCount: 1, items: [20, 30] }]);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('no event when nothing changes', () => {
|
|
286
|
+
let arr = new ReactiveArray(1, 2, 3),
|
|
287
|
+
dispatched = false;
|
|
288
|
+
|
|
289
|
+
arr.on('splice', () => { dispatched = true; });
|
|
290
|
+
arr.splice(1, 0);
|
|
291
|
+
|
|
292
|
+
expect(dispatched).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
describe('concat', () => {
|
|
298
|
+
it('appends arrays', () => {
|
|
299
|
+
let arr = new ReactiveArray(1, 2);
|
|
300
|
+
|
|
301
|
+
arr.concat([3, 4], [5]);
|
|
302
|
+
|
|
303
|
+
expect([...arr]).toEqual([1, 2, 3, 4, 5]);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('appends single values', () => {
|
|
307
|
+
let arr = new ReactiveArray<number>();
|
|
308
|
+
|
|
309
|
+
arr.push(1);
|
|
310
|
+
arr.concat([2], [3]);
|
|
311
|
+
|
|
312
|
+
expect([...arr]).toEqual([1, 2, 3]);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('returns this (mutating)', () => {
|
|
316
|
+
let arr = new ReactiveArray(1);
|
|
317
|
+
let result = arr.concat([2]);
|
|
318
|
+
|
|
319
|
+
expect(result).toBe(arr);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('dispatches concat event', () => {
|
|
323
|
+
let arr = new ReactiveArray<number>(),
|
|
324
|
+
events: number[][] = [];
|
|
325
|
+
|
|
326
|
+
arr.on('concat', (e) => { events.push(e.items); });
|
|
327
|
+
arr.concat([1, 2], [3]);
|
|
328
|
+
|
|
329
|
+
expect(events).toEqual([[1, 2, 3]]);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('no event when nothing added', () => {
|
|
333
|
+
let arr = new ReactiveArray(1),
|
|
334
|
+
dispatched = false;
|
|
335
|
+
|
|
336
|
+
arr.on('concat', () => { dispatched = true; });
|
|
337
|
+
arr.concat([]);
|
|
338
|
+
|
|
339
|
+
expect(dispatched).toBe(false);
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
describe('reverse', () => {
|
|
345
|
+
it('reverses in place', () => {
|
|
346
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
347
|
+
|
|
348
|
+
arr.reverse();
|
|
349
|
+
|
|
350
|
+
expect([...arr]).toEqual([3, 2, 1]);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('returns this', () => {
|
|
354
|
+
let arr = new ReactiveArray(1, 2);
|
|
355
|
+
|
|
356
|
+
expect(arr.reverse()).toBe(arr);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('dispatches reverse event', () => {
|
|
360
|
+
let arr = new ReactiveArray(1, 2),
|
|
361
|
+
dispatched = false;
|
|
362
|
+
|
|
363
|
+
arr.on('reverse', () => { dispatched = true; });
|
|
364
|
+
arr.reverse();
|
|
365
|
+
|
|
366
|
+
expect(dispatched).toBe(true);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
describe('sort', () => {
|
|
372
|
+
it('sorts in place', () => {
|
|
373
|
+
let arr = new ReactiveArray(3, 1, 2);
|
|
374
|
+
|
|
375
|
+
arr.sort((a, b) => a - b);
|
|
376
|
+
|
|
377
|
+
expect([...arr]).toEqual([1, 2, 3]);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('returns this', () => {
|
|
381
|
+
let arr = new ReactiveArray(3, 1);
|
|
382
|
+
|
|
383
|
+
expect(arr.sort()).toBe(arr);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('dispatches sort event with order', () => {
|
|
387
|
+
let arr = new ReactiveArray(3, 1, 2),
|
|
388
|
+
order: number[] = [];
|
|
389
|
+
|
|
390
|
+
arr.on('sort', (e) => { order = e.order; });
|
|
391
|
+
arr.sort((a, b) => a - b);
|
|
392
|
+
|
|
393
|
+
// Before: [3, 1, 2] (indices 0, 1, 2)
|
|
394
|
+
// After: [1, 2, 3] → 1 was at index 1, 2 was at index 2, 3 was at index 0
|
|
395
|
+
expect(order).toEqual([1, 2, 0]);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('handles duplicates in sort', () => {
|
|
399
|
+
let arr = new ReactiveArray(2, 1, 2),
|
|
400
|
+
order: number[] = [];
|
|
401
|
+
|
|
402
|
+
arr.on('sort', (e) => { order = e.order; });
|
|
403
|
+
arr.sort((a, b) => a - b);
|
|
404
|
+
|
|
405
|
+
expect([...arr]).toEqual([1, 2, 2]);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
describe('clear', () => {
|
|
411
|
+
it('empties array', () => {
|
|
412
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
413
|
+
|
|
414
|
+
arr.clear();
|
|
415
|
+
|
|
416
|
+
expect(arr.length).toBe(0);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('dispatches clear event', () => {
|
|
420
|
+
let arr = new ReactiveArray(1, 2),
|
|
421
|
+
dispatched = false;
|
|
422
|
+
|
|
423
|
+
arr.on('clear', () => { dispatched = true; });
|
|
424
|
+
arr.clear();
|
|
425
|
+
|
|
426
|
+
expect(dispatched).toBe(true);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('updates reactive length to 0', async () => {
|
|
430
|
+
let arr = new ReactiveArray(1, 2, 3),
|
|
431
|
+
lengths: number[] = [];
|
|
432
|
+
|
|
433
|
+
effect(() => { lengths.push(arr.$length); });
|
|
434
|
+
arr.clear();
|
|
435
|
+
await Promise.resolve();
|
|
436
|
+
|
|
437
|
+
expect(lengths).toEqual([3, 0]);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
describe('dispose', () => {
|
|
443
|
+
it('empties array', () => {
|
|
444
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
445
|
+
|
|
446
|
+
arr.dispose();
|
|
447
|
+
|
|
448
|
+
expect(arr.length).toBe(0);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('sets reactive length to 0', async () => {
|
|
452
|
+
let arr = new ReactiveArray(1, 2, 3);
|
|
453
|
+
|
|
454
|
+
arr.dispose();
|
|
455
|
+
|
|
456
|
+
expect(arr.$length).toBe(0);
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
describe('event system', () => {
|
|
462
|
+
it('on registers listener', () => {
|
|
463
|
+
let arr = new ReactiveArray<number>(),
|
|
464
|
+
calls = 0;
|
|
465
|
+
|
|
466
|
+
arr.on('push', () => { calls++; });
|
|
467
|
+
arr.push(1);
|
|
468
|
+
arr.push(2);
|
|
469
|
+
|
|
470
|
+
expect(calls).toBe(2);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('on prevents duplicate listeners', () => {
|
|
474
|
+
let arr = new ReactiveArray<number>(),
|
|
475
|
+
calls = 0;
|
|
476
|
+
|
|
477
|
+
let fn = () => { calls++; };
|
|
478
|
+
|
|
479
|
+
arr.on('push', fn);
|
|
480
|
+
arr.on('push', fn);
|
|
481
|
+
arr.push(1);
|
|
482
|
+
|
|
483
|
+
expect(calls).toBe(1);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('once fires only once', () => {
|
|
487
|
+
let arr = new ReactiveArray<number>(),
|
|
488
|
+
calls = 0;
|
|
489
|
+
|
|
490
|
+
arr.once('push', () => { calls++; });
|
|
491
|
+
arr.push(1);
|
|
492
|
+
arr.push(2);
|
|
493
|
+
|
|
494
|
+
expect(calls).toBe(1);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('listener errors are caught and listener removed', () => {
|
|
498
|
+
let arr = new ReactiveArray<number>(),
|
|
499
|
+
calls = 0;
|
|
500
|
+
|
|
501
|
+
arr.on('push', () => { throw new Error('test'); });
|
|
502
|
+
arr.on('push', () => { calls++; });
|
|
503
|
+
|
|
504
|
+
arr.push(1);
|
|
505
|
+
arr.push(2);
|
|
506
|
+
|
|
507
|
+
expect(calls).toBe(2);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('null slots reused for new listeners', () => {
|
|
511
|
+
let arr = new ReactiveArray<number>();
|
|
512
|
+
|
|
513
|
+
let fn1 = () => { throw new Error('remove me'); };
|
|
514
|
+
let fn2 = vi.fn();
|
|
515
|
+
|
|
516
|
+
arr.on('push', fn1);
|
|
517
|
+
arr.push(1); // fn1 throws and gets nulled
|
|
518
|
+
|
|
519
|
+
arr.on('push', fn2);
|
|
520
|
+
arr.push(2);
|
|
521
|
+
|
|
522
|
+
expect(fn2).toHaveBeenCalledTimes(1);
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
describe('reactive() entry point', () => {
|
|
528
|
+
it('creates ReactiveArray via reactive()', () => {
|
|
529
|
+
let arr = reactive([1, 2, 3]);
|
|
530
|
+
|
|
531
|
+
expect(arr).toBeInstanceOf(ReactiveArray);
|
|
532
|
+
expect(arr.length).toBe(3);
|
|
533
|
+
expect([...arr]).toEqual([1, 2, 3]);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it('supports all array operations', () => {
|
|
537
|
+
let arr = reactive([1, 2, 3]);
|
|
538
|
+
|
|
539
|
+
arr.push(4, 5);
|
|
540
|
+
|
|
541
|
+
expect([...arr]).toEqual([1, 2, 3, 4, 5]);
|
|
542
|
+
|
|
543
|
+
arr.pop();
|
|
544
|
+
|
|
545
|
+
expect([...arr]).toEqual([1, 2, 3, 4]);
|
|
546
|
+
|
|
547
|
+
arr.shift();
|
|
548
|
+
|
|
549
|
+
expect([...arr]).toEqual([2, 3, 4]);
|
|
550
|
+
|
|
551
|
+
arr.unshift(0);
|
|
552
|
+
|
|
553
|
+
expect([...arr]).toEqual([0, 2, 3, 4]);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('$set via reactive array', () => {
|
|
557
|
+
let arr = reactive([1, 2, 3]);
|
|
558
|
+
|
|
559
|
+
arr.$set(0, 100);
|
|
560
|
+
|
|
561
|
+
expect(arr[0]).toBe(100);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it('splice via reactive()', () => {
|
|
565
|
+
let arr = reactive(['a', 'b', 'c', 'd', 'e']);
|
|
566
|
+
|
|
567
|
+
let removed = arr.splice(1, 2);
|
|
568
|
+
|
|
569
|
+
expect([...removed]).toEqual(['b', 'c']);
|
|
570
|
+
expect([...arr]).toEqual(['a', 'd', 'e']);
|
|
571
|
+
|
|
572
|
+
arr.splice(1, 0, 'x', 'y');
|
|
573
|
+
|
|
574
|
+
expect([...arr]).toEqual(['a', 'x', 'y', 'd', 'e']);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it('sort and reverse via reactive()', () => {
|
|
578
|
+
let arr = reactive([3, 1, 4, 1, 5, 9, 2, 6]);
|
|
579
|
+
|
|
580
|
+
arr.sort((a, b) => a - b);
|
|
581
|
+
|
|
582
|
+
expect([...arr]).toEqual([1, 1, 2, 3, 4, 5, 6, 9]);
|
|
583
|
+
|
|
584
|
+
arr.reverse();
|
|
585
|
+
|
|
586
|
+
expect([...arr]).toEqual([9, 6, 5, 4, 3, 2, 1, 1]);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('concat with mixed args via reactive()', () => {
|
|
590
|
+
let arr = reactive([1, 2]);
|
|
591
|
+
|
|
592
|
+
arr.concat([3, 4]);
|
|
593
|
+
|
|
594
|
+
expect([...arr]).toEqual([1, 2, 3, 4]);
|
|
595
|
+
|
|
596
|
+
arr.concat([5], [6, 7]);
|
|
597
|
+
|
|
598
|
+
expect([...arr]).toEqual([1, 2, 3, 4, 5, 6, 7]);
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it('events via reactive()', () => {
|
|
602
|
+
let arr = reactive([1, 2, 3]),
|
|
603
|
+
pushEvents: number[][] = [],
|
|
604
|
+
popEvents: { item: number }[] = [],
|
|
605
|
+
setEvents: { index: number; item: number }[] = [];
|
|
606
|
+
|
|
607
|
+
arr.on('push', (e) => { pushEvents.push(e.items); });
|
|
608
|
+
arr.on('pop', (e) => { popEvents.push(e); });
|
|
609
|
+
arr.on('set', (e) => { setEvents.push(e); });
|
|
610
|
+
|
|
611
|
+
arr.push(4, 5);
|
|
612
|
+
arr.pop();
|
|
613
|
+
arr.$set(0, 100);
|
|
614
|
+
|
|
615
|
+
expect(pushEvents).toEqual([[4, 5]]);
|
|
616
|
+
expect(popEvents).toEqual([{ item: 5 }]);
|
|
617
|
+
expect(setEvents).toEqual([{ index: 0, item: 100 }]);
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('clear via reactive()', () => {
|
|
621
|
+
let arr = reactive([1, 2, 3, 4, 5]);
|
|
622
|
+
|
|
623
|
+
expect(arr.length).toBe(5);
|
|
624
|
+
|
|
625
|
+
arr.clear();
|
|
626
|
+
|
|
627
|
+
expect(arr.length).toBe(0);
|
|
628
|
+
expect([...arr]).toEqual([]);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it('reactive length tracking in effects', async () => {
|
|
632
|
+
let arr = reactive([1, 2, 3]),
|
|
633
|
+
lengths: number[] = [];
|
|
634
|
+
|
|
635
|
+
effect(() => {
|
|
636
|
+
lengths.push(arr.$length);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
expect(lengths).toEqual([3]);
|
|
640
|
+
|
|
641
|
+
arr.push(4);
|
|
642
|
+
await Promise.resolve();
|
|
643
|
+
|
|
644
|
+
expect(lengths).toEqual([3, 4]);
|
|
645
|
+
|
|
646
|
+
arr.pop();
|
|
647
|
+
await Promise.resolve();
|
|
648
|
+
|
|
649
|
+
expect(lengths).toEqual([3, 4, 3]);
|
|
650
|
+
|
|
651
|
+
arr.splice(0, 1);
|
|
652
|
+
await Promise.resolve();
|
|
653
|
+
|
|
654
|
+
expect(lengths).toEqual([3, 4, 3, 2]);
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
});
|