ripple 0.2.42 → 0.2.44
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/package.json +1 -1
- package/src/compiler/phases/1-parse/index.js +4 -5
- package/src/compiler/phases/3-transform/index.js +78 -40
- package/src/compiler/phases/3-transform/segments.js +2 -2
- package/src/runtime/array.js +393 -57
- package/src/runtime/index.js +1 -0
- package/src/runtime/internal/client/blocks.js +5 -5
- package/src/runtime/internal/client/constants.js +2 -1
- package/src/runtime/internal/client/index.js +3 -2
- package/src/runtime/internal/client/render.js +6 -6
- package/src/runtime/internal/client/runtime.js +52 -18
- package/src/runtime/internal/client/utils.js +18 -0
- package/tests/__snapshots__/basic.test.ripple.snap +14 -0
- package/tests/array.test.ripple +1424 -10
- package/tests/basic.test.ripple +180 -4
- package/tests/compiler.test.ripple +31 -1
- package/tests/{decorators.test.ripple → ref.test.ripple} +4 -4
- package/types/index.d.ts +2 -0
package/tests/array.test.ripple
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mount, flushSync, effect, untrack, RippleArray } from 'ripple';
|
|
3
|
+
import { ARRAY_SET_INDEX_AT } from '../src/runtime/internal/client/constants.js';
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
describe('array', () => {
|
|
5
|
+
describe('RippleArray', () => {
|
|
6
6
|
let container;
|
|
7
7
|
|
|
8
8
|
function render(component) {
|
|
9
9
|
mount(component, {
|
|
10
|
-
|
|
10
|
+
target: container
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -21,11 +21,11 @@ describe('array', () => {
|
|
|
21
21
|
container = null;
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
it('handles direct assignment', () => {
|
|
24
|
+
it('handles direct assignment and length tracking', () => {
|
|
25
25
|
component ArrayTest() {
|
|
26
26
|
let items = new RippleArray(1, 2, 3);
|
|
27
27
|
|
|
28
|
-
<button onClick={() => items[items
|
|
28
|
+
<button onClick={() => items[items.length] = items.length + 1}>{'increment'}</button>
|
|
29
29
|
|
|
30
30
|
<Child items={items} />
|
|
31
31
|
}
|
|
@@ -35,20 +35,1434 @@ describe('array', () => {
|
|
|
35
35
|
<pre>{items.$length}</pre>
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
render(ArrayTest);
|
|
39
|
+
|
|
40
|
+
const button = container.querySelector('button');
|
|
41
|
+
|
|
42
|
+
button.click();
|
|
43
|
+
flushSync();
|
|
44
|
+
|
|
45
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4]');
|
|
46
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
47
|
+
|
|
48
|
+
button.click();
|
|
49
|
+
flushSync();
|
|
50
|
+
|
|
51
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
52
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('handles push and pop operations with reactivity', () => {
|
|
56
|
+
component ArrayTest() {
|
|
57
|
+
let items = new RippleArray(1, 2, 3);
|
|
58
|
+
let $lastItem = items[items.$length - 1];
|
|
59
|
+
|
|
60
|
+
<button onClick={() => items.push(4)}>{'push'}</button>
|
|
61
|
+
<button onClick={() => items.pop()}>{'pop'}</button>
|
|
62
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
63
|
+
<pre>{items.$length}</pre>
|
|
64
|
+
<pre>{$lastItem}</pre>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
render(ArrayTest);
|
|
68
|
+
|
|
69
|
+
const pushButton = container.querySelectorAll('button')[0];
|
|
70
|
+
const popButton = container.querySelectorAll('button')[1];
|
|
71
|
+
|
|
72
|
+
// Initial state
|
|
73
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3]');
|
|
74
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
75
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('3');
|
|
76
|
+
|
|
77
|
+
// Test push operation
|
|
78
|
+
pushButton.click();
|
|
79
|
+
flushSync();
|
|
80
|
+
|
|
81
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4]');
|
|
82
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
83
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('4');
|
|
84
|
+
|
|
85
|
+
// Test pop operation
|
|
86
|
+
popButton.click();
|
|
87
|
+
flushSync();
|
|
88
|
+
|
|
89
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3]');
|
|
90
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
91
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('3');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('handles shift and unshift operations with reactivity', () => {
|
|
95
|
+
component ArrayTest() {
|
|
96
|
+
let items = new RippleArray(2, 3, 4);
|
|
97
|
+
let $firstItem = items[0];
|
|
98
|
+
|
|
99
|
+
<button onClick={() => items.unshift(1)}>{'unshift'}</button>
|
|
100
|
+
<button onClick={() => items.shift()}>{'shift'}</button>
|
|
101
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
102
|
+
<pre>{items.$length}</pre>
|
|
103
|
+
<pre>{$firstItem}</pre>
|
|
104
|
+
}
|
|
105
|
+
|
|
38
106
|
render(ArrayTest);
|
|
39
107
|
|
|
40
|
-
const
|
|
108
|
+
const unshiftButton = container.querySelectorAll('button')[0];
|
|
109
|
+
const shiftButton = container.querySelectorAll('button')[1];
|
|
41
110
|
|
|
42
|
-
|
|
111
|
+
// Initial state
|
|
112
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[2,3,4]');
|
|
113
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
114
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('2');
|
|
115
|
+
|
|
116
|
+
// Test unshift operation
|
|
117
|
+
unshiftButton.click();
|
|
43
118
|
flushSync();
|
|
44
119
|
|
|
45
120
|
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4]');
|
|
46
121
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
122
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('1');
|
|
47
123
|
|
|
48
|
-
|
|
124
|
+
// Test shift operation
|
|
125
|
+
shiftButton.click();
|
|
49
126
|
flushSync();
|
|
50
127
|
|
|
128
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[2,3,4]');
|
|
129
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
130
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('2');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('handles splice operation with reactivity', () => {
|
|
134
|
+
component ArrayTest() {
|
|
135
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
136
|
+
let $middleItem = items[2];
|
|
137
|
+
|
|
138
|
+
<button onClick={() => items.splice(1, 2, 'a', 'b')}>{'splice'}</button>
|
|
139
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
140
|
+
<pre>{items.$length}</pre>
|
|
141
|
+
<pre>{$middleItem}</pre>
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
render(ArrayTest);
|
|
145
|
+
|
|
146
|
+
const spliceButton = container.querySelector('button');
|
|
147
|
+
|
|
148
|
+
// Initial state
|
|
51
149
|
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
52
150
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
151
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('3');
|
|
152
|
+
|
|
153
|
+
// Test splice operation
|
|
154
|
+
spliceButton.click();
|
|
155
|
+
flushSync();
|
|
156
|
+
|
|
157
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,"a","b",4,5]');
|
|
158
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
159
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('b');
|
|
53
160
|
});
|
|
54
|
-
|
|
161
|
+
|
|
162
|
+
it('handles fill operation with reactivity', () => {
|
|
163
|
+
component ArrayTest() {
|
|
164
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
165
|
+
let $secondItem = items[1];
|
|
166
|
+
|
|
167
|
+
<button onClick={() => items.fill(0, 1, 4)}>{'fill'}</button>
|
|
168
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
169
|
+
<pre>{$secondItem}</pre>
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
render(ArrayTest);
|
|
173
|
+
|
|
174
|
+
const fillButton = container.querySelector('button');
|
|
175
|
+
|
|
176
|
+
// Initial state
|
|
177
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
178
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
179
|
+
|
|
180
|
+
// Test fill operation
|
|
181
|
+
fillButton.click();
|
|
182
|
+
flushSync();
|
|
183
|
+
|
|
184
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,0,0,0,5]');
|
|
185
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('0');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('handles reverse operation with reactivity', () => {
|
|
189
|
+
component ArrayTest() {
|
|
190
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
191
|
+
let $firstItem = items[0];
|
|
192
|
+
let $lastItem = items[4];
|
|
193
|
+
|
|
194
|
+
<button onClick={() => items.reverse()}>{'reverse'}</button>
|
|
195
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
196
|
+
<pre>{$firstItem}</pre>
|
|
197
|
+
<pre>{$lastItem}</pre>
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
render(ArrayTest);
|
|
201
|
+
|
|
202
|
+
const reverseButton = container.querySelector('button');
|
|
203
|
+
|
|
204
|
+
// Initial state
|
|
205
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
206
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('1');
|
|
207
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('5');
|
|
208
|
+
|
|
209
|
+
// Test reverse operation
|
|
210
|
+
reverseButton.click();
|
|
211
|
+
flushSync();
|
|
212
|
+
|
|
213
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[5,4,3,2,1]');
|
|
214
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
215
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('1');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('handles sort operation with reactivity', () => {
|
|
219
|
+
component ArrayTest() {
|
|
220
|
+
let items = new RippleArray(5, 3, 1, 4, 2);
|
|
221
|
+
let $secondItem = items[1];
|
|
222
|
+
|
|
223
|
+
<button onClick={() => items.sort()}>{'sort ascending'}</button>
|
|
224
|
+
<button onClick={() => items.sort((a, b) => b - a)}>{'sort descending'}</button>
|
|
225
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
226
|
+
<pre>{$secondItem}</pre>
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
render(ArrayTest);
|
|
230
|
+
|
|
231
|
+
const sortAscButton = container.querySelectorAll('button')[0];
|
|
232
|
+
const sortDescButton = container.querySelectorAll('button')[1];
|
|
233
|
+
|
|
234
|
+
// Initial state
|
|
235
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[5,3,1,4,2]');
|
|
236
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
237
|
+
|
|
238
|
+
// Test sort ascending
|
|
239
|
+
sortAscButton.click();
|
|
240
|
+
flushSync();
|
|
241
|
+
|
|
242
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
243
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
244
|
+
|
|
245
|
+
// Test sort descending
|
|
246
|
+
sortDescButton.click();
|
|
247
|
+
flushSync();
|
|
248
|
+
|
|
249
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[5,4,3,2,1]');
|
|
250
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('handles array methods that return values (map, filter, etc.)', () => {
|
|
254
|
+
component ArrayTest() {
|
|
255
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
256
|
+
let $doubled = items.map(x => x * 2);
|
|
257
|
+
let $filtered = items.filter(x => (x % 2) === 0);
|
|
258
|
+
let $reduced = items.reduce((acc, val) => acc + val, 0);
|
|
259
|
+
let $includes = items.includes(3);
|
|
260
|
+
|
|
261
|
+
<button onClick={() => items.push(6)}>{'add item'}</button>
|
|
262
|
+
<pre>{JSON.stringify($doubled)}</pre>
|
|
263
|
+
<pre>{JSON.stringify($filtered)}</pre>
|
|
264
|
+
<pre>{$reduced}</pre>
|
|
265
|
+
<pre>{$includes.toString()}</pre>
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
render(ArrayTest);
|
|
269
|
+
|
|
270
|
+
const addButton = container.querySelector('button');
|
|
271
|
+
|
|
272
|
+
// Initial state
|
|
273
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[2,4,6,8,10]');
|
|
274
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[2,4]');
|
|
275
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('15');
|
|
276
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('true');
|
|
277
|
+
|
|
278
|
+
// Test reactivity with these methods
|
|
279
|
+
addButton.click();
|
|
280
|
+
flushSync();
|
|
281
|
+
|
|
282
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[2,4,6,8,10,12]');
|
|
283
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[2,4,6]');
|
|
284
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('21');
|
|
285
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('true');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('handles array modification through forEach()', () => {
|
|
289
|
+
component ArrayTest() {
|
|
290
|
+
let items = new RippleArray(1, 2, 3);
|
|
291
|
+
|
|
292
|
+
<button onClick={() => items.forEach((item, i) => items[i] = item * 2)}>{'double all'}</button>
|
|
293
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
render(ArrayTest);
|
|
297
|
+
|
|
298
|
+
const doubleButton = container.querySelector('button');
|
|
299
|
+
|
|
300
|
+
// Initial state
|
|
301
|
+
expect(container.querySelector('pre').textContent).toBe('[1,2,3]');
|
|
302
|
+
|
|
303
|
+
// Test iteration with side effects
|
|
304
|
+
doubleButton.click();
|
|
305
|
+
flushSync();
|
|
306
|
+
|
|
307
|
+
expect(container.querySelector('pre').textContent).toBe('[2,4,6]');
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('handles entries method with reactivity', () => {
|
|
311
|
+
component ArrayTest() {
|
|
312
|
+
let items = new RippleArray('a', 'b', 'c');
|
|
313
|
+
let $entries = Array.from(items.entries());
|
|
314
|
+
|
|
315
|
+
<button onClick={() => items.push('d')}>{'add item'}</button>
|
|
316
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
317
|
+
<pre>{JSON.stringify($entries)}</pre>
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
render(ArrayTest);
|
|
321
|
+
|
|
322
|
+
const addButton = container.querySelector('button');
|
|
323
|
+
|
|
324
|
+
// Initial state
|
|
325
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["a","b","c"]');
|
|
326
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[[0,"a"],[1,"b"],[2,"c"]]');
|
|
327
|
+
|
|
328
|
+
// Test adding an item
|
|
329
|
+
addButton.click();
|
|
330
|
+
flushSync();
|
|
331
|
+
|
|
332
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["a","b","c","d"]');
|
|
333
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[[0,"a"],[1,"b"],[2,"c"],[3,"d"]]');
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('handles concat method with reactivity', () => {
|
|
337
|
+
component ArrayTest() {
|
|
338
|
+
let items = new RippleArray(1, 2, 3);
|
|
339
|
+
let $concatenated = items.concat([4, 5], 6, [7, 8]);
|
|
340
|
+
|
|
341
|
+
<button onClick={() => items.push(3.5)}>{'add to original'}</button>
|
|
342
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
343
|
+
<pre>{JSON.stringify($concatenated)}</pre>
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
render(ArrayTest);
|
|
347
|
+
|
|
348
|
+
const addButton = container.querySelector('button');
|
|
349
|
+
|
|
350
|
+
// Initial state
|
|
351
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3]');
|
|
352
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,3,4,5,6,7,8]');
|
|
353
|
+
|
|
354
|
+
// Test adding to original array
|
|
355
|
+
addButton.click();
|
|
356
|
+
flushSync();
|
|
357
|
+
|
|
358
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,3.5]');
|
|
359
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,3,3.5,4,5,6,7,8]');
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('handles array modification through iterator', () => {
|
|
363
|
+
component ArrayTest() {
|
|
364
|
+
let items = new RippleArray(1, 2, 3);
|
|
365
|
+
|
|
366
|
+
<button onClick={() => items.forEach((item, i) => items[i] = item * 2)}>{'double all'}</button>
|
|
367
|
+
|
|
368
|
+
for (const item of items) {
|
|
369
|
+
<pre>{item}</pre>
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
render(ArrayTest);
|
|
374
|
+
|
|
375
|
+
const doubleButton = container.querySelector('button');
|
|
376
|
+
|
|
377
|
+
// Initial state
|
|
378
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('1');
|
|
379
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
380
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('3');
|
|
381
|
+
|
|
382
|
+
// Test iteration with side effects
|
|
383
|
+
doubleButton.click();
|
|
384
|
+
flushSync();
|
|
385
|
+
|
|
386
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('2');
|
|
387
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
388
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('6');
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('handles $length property for reactivity', () => {
|
|
392
|
+
component ArrayTest() {
|
|
393
|
+
let items = new RippleArray(1, 2, 3);
|
|
394
|
+
let $length = items.$length;
|
|
395
|
+
|
|
396
|
+
<button onClick={() => items.$length = 5}>{'expand'}</button>
|
|
397
|
+
<button onClick={() => items.$length = 2}>{'shrink'}</button>
|
|
398
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
399
|
+
<pre>{$length}</pre>
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
render(ArrayTest);
|
|
403
|
+
|
|
404
|
+
const expandButton = container.querySelectorAll('button')[0];
|
|
405
|
+
const shrinkButton = container.querySelectorAll('button')[1];
|
|
406
|
+
|
|
407
|
+
// Initial state
|
|
408
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3]');
|
|
409
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
410
|
+
|
|
411
|
+
// Test expand
|
|
412
|
+
expandButton.click();
|
|
413
|
+
flushSync();
|
|
414
|
+
|
|
415
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,null,null]');
|
|
416
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
417
|
+
|
|
418
|
+
// Test shrink
|
|
419
|
+
shrinkButton.click();
|
|
420
|
+
flushSync();
|
|
421
|
+
|
|
422
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2]');
|
|
423
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('handles static methods - from and of', () => {
|
|
427
|
+
component ArrayTest() {
|
|
428
|
+
let itemsFrom = RippleArray.from([1, 2, 3], x => x * 2);
|
|
429
|
+
let itemsOf = RippleArray.of(4, 5, 6);
|
|
430
|
+
|
|
431
|
+
<button onClick={() => itemsFrom.push(8)}>{'add to from'}</button>
|
|
432
|
+
<button onClick={() => itemsOf.push(7)}>{'add to of'}</button>
|
|
433
|
+
<pre>{JSON.stringify(itemsFrom)}</pre>
|
|
434
|
+
<pre>{JSON.stringify(itemsOf)}</pre>
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
render(ArrayTest);
|
|
438
|
+
|
|
439
|
+
const addFromButton = container.querySelectorAll('button')[0];
|
|
440
|
+
const addOfButton = container.querySelectorAll('button')[1];
|
|
441
|
+
|
|
442
|
+
// Initial state
|
|
443
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[2,4,6]');
|
|
444
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[4,5,6]');
|
|
445
|
+
|
|
446
|
+
// Test adding to from-created array
|
|
447
|
+
addFromButton.click();
|
|
448
|
+
flushSync();
|
|
449
|
+
|
|
450
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[2,4,6,8]');
|
|
451
|
+
|
|
452
|
+
// Test adding to of-created array
|
|
453
|
+
addOfButton.click();
|
|
454
|
+
flushSync();
|
|
455
|
+
|
|
456
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[4,5,6,7]');
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('handles toJSON method', () => {
|
|
460
|
+
component ArrayTest() {
|
|
461
|
+
let items = new RippleArray(1, 2, 3);
|
|
462
|
+
|
|
463
|
+
<button onClick={() => items.push(4)}>{'add'}</button>
|
|
464
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
render(ArrayTest);
|
|
468
|
+
|
|
469
|
+
const addButton = container.querySelector('button');
|
|
470
|
+
|
|
471
|
+
// Initial state - toJSON is implicitly called by JSON.stringify
|
|
472
|
+
expect(container.querySelector('pre').textContent).toBe('[1,2,3]');
|
|
473
|
+
|
|
474
|
+
// Test reactivity with JSON serialization
|
|
475
|
+
addButton.click();
|
|
476
|
+
flushSync();
|
|
477
|
+
|
|
478
|
+
expect(container.querySelector('pre').textContent).toBe('[1,2,3,4]');
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('handles array index access with reactivity', () => {
|
|
482
|
+
component ArrayTest() {
|
|
483
|
+
let items = new RippleArray(10, 20, 30);
|
|
484
|
+
let $firstItem = items[0];
|
|
485
|
+
let $secondItem = items[1];
|
|
486
|
+
|
|
487
|
+
<button onClick={() => items[0] = 100}>{'change first'}</button>
|
|
488
|
+
<pre>{$firstItem}</pre>
|
|
489
|
+
<pre>{$secondItem}</pre>
|
|
490
|
+
<pre>{items[0]}</pre>
|
|
491
|
+
<pre>{items[1]}</pre>
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
render(ArrayTest);
|
|
495
|
+
|
|
496
|
+
const changeButton = container.querySelector('button');
|
|
497
|
+
|
|
498
|
+
// Initial state
|
|
499
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('10');
|
|
500
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('20');
|
|
501
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('10');
|
|
502
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('20');
|
|
503
|
+
|
|
504
|
+
// Test changing array element directly
|
|
505
|
+
changeButton.click();
|
|
506
|
+
flushSync();
|
|
507
|
+
|
|
508
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('100');
|
|
509
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('20');
|
|
510
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('100');
|
|
511
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('20');
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('handles array slice method with reactivity', () => {
|
|
515
|
+
component ArrayTest() {
|
|
516
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
517
|
+
let $sliced = items.slice(1, 4);
|
|
518
|
+
|
|
519
|
+
<button onClick={() => items[2] = 30}>{'change middle'}</button>
|
|
520
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
521
|
+
<pre>{JSON.stringify($sliced)}</pre>
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
render(ArrayTest);
|
|
525
|
+
|
|
526
|
+
const changeButton = container.querySelector('button');
|
|
527
|
+
|
|
528
|
+
// Initial state
|
|
529
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
530
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[2,3,4]');
|
|
531
|
+
|
|
532
|
+
// Test reactivity with slice
|
|
533
|
+
changeButton.click();
|
|
534
|
+
flushSync();
|
|
535
|
+
|
|
536
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,30,4,5]');
|
|
537
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[2,30,4]');
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('handles find and findIndex methods with reactivity', () => {
|
|
541
|
+
component ArrayTest() {
|
|
542
|
+
let items = new RippleArray(5, 10, 15, 20, 25);
|
|
543
|
+
let $found = items.find(x => x > 12);
|
|
544
|
+
let $foundIndex = items.findIndex(x => x > 12);
|
|
545
|
+
|
|
546
|
+
<button onClick={() => {
|
|
547
|
+
items[1] = 13;
|
|
548
|
+
items[0] = 6;
|
|
549
|
+
}}>{'update values'}</button>
|
|
550
|
+
<pre>{$found}</pre>
|
|
551
|
+
<pre>{$foundIndex}</pre>
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
render(ArrayTest);
|
|
555
|
+
|
|
556
|
+
const updateButton = container.querySelector('button');
|
|
557
|
+
|
|
558
|
+
// Initial state
|
|
559
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('15');
|
|
560
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
561
|
+
|
|
562
|
+
// Test reactivity with find methods
|
|
563
|
+
updateButton.click();
|
|
564
|
+
flushSync();
|
|
565
|
+
|
|
566
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('13');
|
|
567
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('1');
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
// Additional tests for RippleArray methods
|
|
571
|
+
|
|
572
|
+
it('handles findLast and findLastIndex methods with reactivity', () => {
|
|
573
|
+
component ArrayTest() {
|
|
574
|
+
let items = new RippleArray(5, 15, 10, 20, 15);
|
|
575
|
+
let $foundLast = items.findLast(x => x === 15);
|
|
576
|
+
let $foundLastIndex = items.findLastIndex(x => x === 15);
|
|
577
|
+
|
|
578
|
+
<button onClick={() => {
|
|
579
|
+
items[1] = 25;
|
|
580
|
+
items[4] = 15;
|
|
581
|
+
}}>{'update values'}</button>
|
|
582
|
+
<pre>{$foundLast}</pre>
|
|
583
|
+
<pre>{$foundLastIndex}</pre>
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
render(ArrayTest);
|
|
587
|
+
|
|
588
|
+
const updateButton = container.querySelector('button');
|
|
589
|
+
|
|
590
|
+
// Initial state
|
|
591
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('15');
|
|
592
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
593
|
+
|
|
594
|
+
// Test reactivity with findLast methods
|
|
595
|
+
updateButton.click();
|
|
596
|
+
flushSync();
|
|
597
|
+
|
|
598
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('15');
|
|
599
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('handles every method with reactivity', () => {
|
|
603
|
+
component ArrayTest() {
|
|
604
|
+
let items = new RippleArray(2, 4, 6, 8);
|
|
605
|
+
let $allEven = items.every(x => x % 2 === 0);
|
|
606
|
+
|
|
607
|
+
<button onClick={() => items.push(3)}>{'add odd'}</button>
|
|
608
|
+
<button onClick={() => {
|
|
609
|
+
items.pop();
|
|
610
|
+
items.push(10);
|
|
611
|
+
}}>{'ensure all even'}</button>
|
|
612
|
+
<pre>{$allEven.toString()}</pre>
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
render(ArrayTest);
|
|
616
|
+
|
|
617
|
+
const addOddButton = container.querySelectorAll('button')[0];
|
|
618
|
+
const makeEvenButton = container.querySelectorAll('button')[1];
|
|
619
|
+
|
|
620
|
+
// Initial state
|
|
621
|
+
expect(container.querySelector('pre').textContent).toBe('true');
|
|
622
|
+
|
|
623
|
+
// Test adding an odd number
|
|
624
|
+
addOddButton.click();
|
|
625
|
+
flushSync();
|
|
626
|
+
expect(container.querySelector('pre').textContent).toBe('false');
|
|
627
|
+
|
|
628
|
+
// Test fixing the array to all even
|
|
629
|
+
makeEvenButton.click();
|
|
630
|
+
flushSync();
|
|
631
|
+
expect(container.querySelector('pre').textContent).toBe('true');
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it('handles flat method with reactivity', () => {
|
|
635
|
+
component ArrayTest() {
|
|
636
|
+
let items = new RippleArray([1, 2], [3, 4], 5);
|
|
637
|
+
let $flattened = items.flat();
|
|
638
|
+
|
|
639
|
+
<button onClick={() => items[0] = [6, 7, 8]}>{'change nested'}</button>
|
|
640
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
641
|
+
<pre>{JSON.stringify($flattened)}</pre>
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
render(ArrayTest);
|
|
645
|
+
|
|
646
|
+
const changeButton = container.querySelector('button');
|
|
647
|
+
|
|
648
|
+
// Initial state
|
|
649
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[[1,2],[3,4],5]');
|
|
650
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,3,4,5]');
|
|
651
|
+
|
|
652
|
+
// Test changing a nested array
|
|
653
|
+
changeButton.click();
|
|
654
|
+
flushSync();
|
|
655
|
+
|
|
656
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[[6,7,8],[3,4],5]');
|
|
657
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[6,7,8,3,4,5]');
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
it('handles flatMap method with reactivity', () => {
|
|
661
|
+
component ArrayTest() {
|
|
662
|
+
let items = new RippleArray(1, 2, 3);
|
|
663
|
+
let $flatMapped = items.flatMap(x => [x, x * 2]);
|
|
664
|
+
|
|
665
|
+
<button onClick={() => items.push(4)}>{'add item'}</button>
|
|
666
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
667
|
+
<pre>{JSON.stringify($flatMapped)}</pre>
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
render(ArrayTest);
|
|
671
|
+
|
|
672
|
+
const addButton = container.querySelector('button');
|
|
673
|
+
|
|
674
|
+
// Initial state
|
|
675
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3]');
|
|
676
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,2,4,3,6]');
|
|
677
|
+
|
|
678
|
+
// Test adding an item
|
|
679
|
+
addButton.click();
|
|
680
|
+
flushSync();
|
|
681
|
+
|
|
682
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4]');
|
|
683
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,2,4,3,6,4,8]');
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
it('handles join method with reactivity', () => {
|
|
687
|
+
component ArrayTest() {
|
|
688
|
+
let items = new RippleArray('apple', 'banana', 'cherry');
|
|
689
|
+
let $joined = items.join(', ');
|
|
690
|
+
|
|
691
|
+
<button onClick={() => items.push('date')}>{'add item'}</button>
|
|
692
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
693
|
+
<pre>{$joined}</pre>
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
render(ArrayTest);
|
|
697
|
+
|
|
698
|
+
const addButton = container.querySelector('button');
|
|
699
|
+
|
|
700
|
+
// Initial state
|
|
701
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["apple","banana","cherry"]');
|
|
702
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('apple, banana, cherry');
|
|
703
|
+
|
|
704
|
+
// Test adding an item
|
|
705
|
+
addButton.click();
|
|
706
|
+
flushSync();
|
|
707
|
+
|
|
708
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["apple","banana","cherry","date"]');
|
|
709
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('apple, banana, cherry, date');
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
it('handles keys method with reactivity', () => {
|
|
713
|
+
component ArrayTest() {
|
|
714
|
+
let items = new RippleArray('a', 'b', 'c');
|
|
715
|
+
let $keys = Array.from(items.keys());
|
|
716
|
+
|
|
717
|
+
<button onClick={() => items.push('d')}>{'add item'}</button>
|
|
718
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
719
|
+
<pre>{JSON.stringify($keys)}</pre>
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
render(ArrayTest);
|
|
723
|
+
|
|
724
|
+
const addButton = container.querySelector('button');
|
|
725
|
+
|
|
726
|
+
// Initial state
|
|
727
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["a","b","c"]');
|
|
728
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[0,1,2]');
|
|
729
|
+
|
|
730
|
+
// Test adding an item
|
|
731
|
+
addButton.click();
|
|
732
|
+
flushSync();
|
|
733
|
+
|
|
734
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["a","b","c","d"]');
|
|
735
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[0,1,2,3]');
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
it('handles lastIndexOf method with reactivity', () => {
|
|
739
|
+
component ArrayTest() {
|
|
740
|
+
let items = new RippleArray(1, 2, 3, 2, 1);
|
|
741
|
+
let $lastIndex = items.lastIndexOf(2);
|
|
742
|
+
|
|
743
|
+
<button onClick={() => {
|
|
744
|
+
items.push(2);
|
|
745
|
+
}}>{'add duplicate'}</button>
|
|
746
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
747
|
+
<pre>{$lastIndex}</pre>
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
render(ArrayTest);
|
|
751
|
+
|
|
752
|
+
const addButton = container.querySelector('button');
|
|
753
|
+
|
|
754
|
+
// Initial state
|
|
755
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,2,1]');
|
|
756
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
757
|
+
|
|
758
|
+
// Test adding a duplicate
|
|
759
|
+
addButton.click();
|
|
760
|
+
flushSync();
|
|
761
|
+
|
|
762
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,2,1,2]');
|
|
763
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
it('handles reduceRight method with reactivity', () => {
|
|
767
|
+
component ArrayTest() {
|
|
768
|
+
let items = new RippleArray('a', 'b', 'c');
|
|
769
|
+
let $reduced = items.reduceRight((acc, val) => acc + val, '');
|
|
770
|
+
|
|
771
|
+
<button onClick={() => items.push('d')}>{'add item'}</button>
|
|
772
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
773
|
+
<pre>{$reduced}</pre>
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
render(ArrayTest);
|
|
777
|
+
|
|
778
|
+
const addButton = container.querySelector('button');
|
|
779
|
+
|
|
780
|
+
// Initial state
|
|
781
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["a","b","c"]');
|
|
782
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('cba');
|
|
783
|
+
|
|
784
|
+
// Test adding an item
|
|
785
|
+
addButton.click();
|
|
786
|
+
flushSync();
|
|
787
|
+
|
|
788
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["a","b","c","d"]');
|
|
789
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('dcba');
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
it('handles some method with reactivity', () => {
|
|
793
|
+
component ArrayTest() {
|
|
794
|
+
let items = new RippleArray(1, 3, 5, 7);
|
|
795
|
+
let $hasEven = items.some(x => x % 2 === 0);
|
|
796
|
+
|
|
797
|
+
<button onClick={() => items.push(2)}>{'add even'}</button>
|
|
798
|
+
<button onClick={() => {
|
|
799
|
+
items.pop();
|
|
800
|
+
items.push(9);
|
|
801
|
+
}}>{'ensure all odd'}</button>
|
|
802
|
+
<pre>{$hasEven.toString()}</pre>
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
render(ArrayTest);
|
|
806
|
+
|
|
807
|
+
const addEvenButton = container.querySelectorAll('button')[0];
|
|
808
|
+
const makeOddButton = container.querySelectorAll('button')[1];
|
|
809
|
+
|
|
810
|
+
// Initial state
|
|
811
|
+
expect(container.querySelector('pre').textContent).toBe('false');
|
|
812
|
+
|
|
813
|
+
// Test adding an even number
|
|
814
|
+
addEvenButton.click();
|
|
815
|
+
flushSync();
|
|
816
|
+
expect(container.querySelector('pre').textContent).toBe('true');
|
|
817
|
+
|
|
818
|
+
// Test fixing the array to all odd
|
|
819
|
+
makeOddButton.click();
|
|
820
|
+
flushSync();
|
|
821
|
+
expect(container.querySelector('pre').textContent).toBe('false');
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
it('handles toLocaleString method with reactivity', () => {
|
|
825
|
+
component ArrayTest() {
|
|
826
|
+
let items = new RippleArray(1000, 2000, 3000);
|
|
827
|
+
let $localized = items.toLocaleString('en-US');
|
|
828
|
+
|
|
829
|
+
<button onClick={() => {items[2] = 4000}}>{'add item'}</button>
|
|
830
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
831
|
+
<pre>{$localized}</pre>
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
render(ArrayTest);
|
|
835
|
+
|
|
836
|
+
const addButton = container.querySelector('button');
|
|
837
|
+
|
|
838
|
+
// Initial state
|
|
839
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1000,2000,3000]');
|
|
840
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('1,000,2,000,3,000');
|
|
841
|
+
|
|
842
|
+
// Test adding an item
|
|
843
|
+
addButton.click();
|
|
844
|
+
flushSync();
|
|
845
|
+
|
|
846
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1000,2000,4000]');
|
|
847
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('1,000,2,000,4,000');
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
it('handles toReversed method with reactivity', (context) => {
|
|
851
|
+
if (!('toReversed' in Array.prototype)) {
|
|
852
|
+
context.skip();
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
component ArrayTest() {
|
|
856
|
+
let items = new RippleArray(1, 2, 3, 4);
|
|
857
|
+
let $reversed = items.toReversed();
|
|
858
|
+
|
|
859
|
+
<button onClick={() => items.push(5)}>{'add item'}</button>
|
|
860
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
861
|
+
<pre>{JSON.stringify($reversed)}</pre>
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
render(ArrayTest);
|
|
865
|
+
|
|
866
|
+
const addButton = container.querySelector('button');
|
|
867
|
+
|
|
868
|
+
// Initial state
|
|
869
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4]');
|
|
870
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[4,3,2,1]');
|
|
871
|
+
|
|
872
|
+
// Test adding an item
|
|
873
|
+
addButton.click();
|
|
874
|
+
flushSync();
|
|
875
|
+
|
|
876
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
877
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[5,4,3,2,1]');
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
it('handles toSorted method with reactivity', () => {
|
|
881
|
+
if (!('toSorted' in Array.prototype)) {
|
|
882
|
+
context.skip();
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
component ArrayTest() {
|
|
886
|
+
let items = new RippleArray(3, 1, 4, 2);
|
|
887
|
+
let $sorted = items.toSorted();
|
|
888
|
+
|
|
889
|
+
<button onClick={() => items.push(0)}>{'add item'}</button>
|
|
890
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
891
|
+
<pre>{JSON.stringify($sorted)}</pre>
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
render(ArrayTest);
|
|
895
|
+
|
|
896
|
+
const addButton = container.querySelector('button');
|
|
897
|
+
|
|
898
|
+
// Initial state
|
|
899
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[3,1,4,2]');
|
|
900
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,3,4]');
|
|
901
|
+
|
|
902
|
+
// Test adding an item
|
|
903
|
+
addButton.click();
|
|
904
|
+
flushSync();
|
|
905
|
+
|
|
906
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[3,1,4,2,0]');
|
|
907
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[0,1,2,3,4]');
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
it('handles toSpliced method with reactivity', () => {
|
|
911
|
+
if (!('toSpliced' in Array.prototype)) {
|
|
912
|
+
context.skip();
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
component ArrayTest() {
|
|
916
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
917
|
+
let $spliced = items.toSpliced(1, 2, 'a', 'b');
|
|
918
|
+
|
|
919
|
+
<button onClick={() => items[2] = 30}>{'change item'}</button>
|
|
920
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
921
|
+
<pre>{JSON.stringify($spliced)}</pre>
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
render(ArrayTest);
|
|
925
|
+
|
|
926
|
+
const changeButton = container.querySelector('button');
|
|
927
|
+
|
|
928
|
+
// Initial state
|
|
929
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
930
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,"a","b",4,5]');
|
|
931
|
+
|
|
932
|
+
// Test changing an item
|
|
933
|
+
changeButton.click();
|
|
934
|
+
flushSync();
|
|
935
|
+
|
|
936
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,30,4,5]');
|
|
937
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,"a","b",4,5]');
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
it('handles toString method with reactivity', () => {
|
|
941
|
+
component ArrayTest() {
|
|
942
|
+
let items = new RippleArray(1, 2, 3);
|
|
943
|
+
let $string = items.toString();
|
|
944
|
+
|
|
945
|
+
<button onClick={() => items.push(4)}>{'add item'}</button>
|
|
946
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
947
|
+
<pre>{$string}</pre>
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
render(ArrayTest);
|
|
951
|
+
|
|
952
|
+
const addButton = container.querySelector('button');
|
|
953
|
+
|
|
954
|
+
// Initial state
|
|
955
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3]');
|
|
956
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('1,2,3');
|
|
957
|
+
|
|
958
|
+
// Test adding an item
|
|
959
|
+
addButton.click();
|
|
960
|
+
flushSync();
|
|
961
|
+
|
|
962
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4]');
|
|
963
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('1,2,3,4');
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
it('handles Symbol.iterator with reactivity', () => {
|
|
967
|
+
component ArrayTest() {
|
|
968
|
+
let items = new RippleArray(1, 2, 3);
|
|
969
|
+
let $sum = 0;
|
|
970
|
+
|
|
971
|
+
effect(() => {
|
|
972
|
+
$sum = 0;
|
|
973
|
+
for (const item of items) {
|
|
974
|
+
untrack(() => {
|
|
975
|
+
$sum += item;
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
<button onClick={() => items.push(4)}>{'add item'}</button>
|
|
981
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
982
|
+
<pre>{$sum}</pre>
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
render(ArrayTest);
|
|
986
|
+
flushSync();
|
|
987
|
+
|
|
988
|
+
const addButton = container.querySelectorAll('button')[0];
|
|
989
|
+
|
|
990
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3]');
|
|
991
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('6');
|
|
992
|
+
|
|
993
|
+
addButton.click();
|
|
994
|
+
flushSync();
|
|
995
|
+
|
|
996
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4]');
|
|
997
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('10');
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
it('handles values method with reactivity', () => {
|
|
1001
|
+
component ArrayTest() {
|
|
1002
|
+
let items = new RippleArray('a', 'b', 'c');
|
|
1003
|
+
let $values = Array.from(items.values());
|
|
1004
|
+
|
|
1005
|
+
<button onClick={() => items.push('d')}>{'add item'}</button>
|
|
1006
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
1007
|
+
<pre>{JSON.stringify($values)}</pre>
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
render(ArrayTest);
|
|
1011
|
+
|
|
1012
|
+
const addButton = container.querySelector('button');
|
|
1013
|
+
|
|
1014
|
+
// Initial state
|
|
1015
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["a","b","c"]');
|
|
1016
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('["a","b","c"]');
|
|
1017
|
+
|
|
1018
|
+
// Test adding an item
|
|
1019
|
+
addButton.click();
|
|
1020
|
+
flushSync();
|
|
1021
|
+
|
|
1022
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('["a","b","c","d"]');
|
|
1023
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('["a","b","c","d"]');
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
it('handles with method with reactivity', (context) => {
|
|
1027
|
+
if (!('with' in Array.prototype)) {
|
|
1028
|
+
context.skip();
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
component ArrayTest() {
|
|
1032
|
+
let items = new RippleArray(1, 2, 3, 4);
|
|
1033
|
+
let $withReplaced = items.with(2, 30);
|
|
1034
|
+
|
|
1035
|
+
<button onClick={() => items[2] = 50}>{'change original'}</button>
|
|
1036
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
1037
|
+
<pre>{JSON.stringify($withReplaced)}</pre>
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
render(ArrayTest);
|
|
1041
|
+
|
|
1042
|
+
const changeButton = container.querySelector('button');
|
|
1043
|
+
|
|
1044
|
+
// Initial state
|
|
1045
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4]');
|
|
1046
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,30,4]');
|
|
1047
|
+
|
|
1048
|
+
// Test changing the original array
|
|
1049
|
+
changeButton.click();
|
|
1050
|
+
flushSync();
|
|
1051
|
+
|
|
1052
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,50,4]');
|
|
1053
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,30,4]');
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
it('handles at method with reactivity', () => {
|
|
1057
|
+
component ArrayTest() {
|
|
1058
|
+
let items = new RippleArray(10, 20, 30, 40, 50);
|
|
1059
|
+
let $atIndex2 = items.at(2);
|
|
1060
|
+
let $atNegative1 = items.at(-1);
|
|
1061
|
+
let $atNegative2 = items.at(-2);
|
|
1062
|
+
|
|
1063
|
+
<button onClick={() => items[2] = 300}>{'change index 2'}</button>
|
|
1064
|
+
<button onClick={() => items[items.length - 1] = 500}>{'change last'}</button>
|
|
1065
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
1066
|
+
<pre>{$atIndex2}</pre>
|
|
1067
|
+
<pre>{$atNegative1}</pre>
|
|
1068
|
+
<pre>{$atNegative2}</pre>
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
render(ArrayTest);
|
|
1072
|
+
|
|
1073
|
+
const changeIndex2Button = container.querySelectorAll('button')[0];
|
|
1074
|
+
const changeLastButton = container.querySelectorAll('button')[1];
|
|
1075
|
+
|
|
1076
|
+
// Initial state
|
|
1077
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[10,20,30,40,50]');
|
|
1078
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('30');
|
|
1079
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('50');
|
|
1080
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('40');
|
|
1081
|
+
|
|
1082
|
+
// Test changing index 2
|
|
1083
|
+
changeIndex2Button.click();
|
|
1084
|
+
flushSync();
|
|
1085
|
+
|
|
1086
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[10,20,300,40,50]');
|
|
1087
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('300');
|
|
1088
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('50');
|
|
1089
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('40');
|
|
1090
|
+
|
|
1091
|
+
// Test changing last item
|
|
1092
|
+
changeLastButton.click();
|
|
1093
|
+
flushSync();
|
|
1094
|
+
|
|
1095
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[10,20,300,40,500]');
|
|
1096
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('300');
|
|
1097
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('500');
|
|
1098
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('40');
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
it('handles ARRAY_SET_INDEX_AT method with reactivity', () => {
|
|
1102
|
+
component ArrayTest() {
|
|
1103
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
1104
|
+
let $thirdItem = items[2];
|
|
1105
|
+
|
|
1106
|
+
<button onClick={() => items[ARRAY_SET_INDEX_AT](2, 30)}>{'set index 2'}</button>
|
|
1107
|
+
<button onClick={() => items[ARRAY_SET_INDEX_AT](-1, 50)}>{'set last with negative'}</button>
|
|
1108
|
+
<button onClick={() => items[ARRAY_SET_INDEX_AT](6, 60)}>{'extend array'}</button>
|
|
1109
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
1110
|
+
<pre>{items.$length}</pre>
|
|
1111
|
+
<pre>{$thirdItem}</pre>
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
render(ArrayTest);
|
|
1115
|
+
|
|
1116
|
+
const setIndex2Button = container.querySelectorAll('button')[0];
|
|
1117
|
+
const setLastButton = container.querySelectorAll('button')[1];
|
|
1118
|
+
const extendButton = container.querySelectorAll('button')[2];
|
|
1119
|
+
|
|
1120
|
+
// Initial state
|
|
1121
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
1122
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
1123
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('3');
|
|
1124
|
+
|
|
1125
|
+
// Test setting index 2
|
|
1126
|
+
setIndex2Button.click();
|
|
1127
|
+
flushSync();
|
|
1128
|
+
|
|
1129
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,30,4,5]');
|
|
1130
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
1131
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('30');
|
|
1132
|
+
|
|
1133
|
+
// Test setting with negative index
|
|
1134
|
+
setLastButton.click();
|
|
1135
|
+
flushSync();
|
|
1136
|
+
|
|
1137
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,30,4,50]');
|
|
1138
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
1139
|
+
|
|
1140
|
+
// Test extending array
|
|
1141
|
+
extendButton.click();
|
|
1142
|
+
flushSync();
|
|
1143
|
+
|
|
1144
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,30,4,50,null,60]');
|
|
1145
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('7');
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
it('handles ARRAY_SET_INDEX_AT error cases', () => {
|
|
1149
|
+
component ArrayTest() {
|
|
1150
|
+
let items = new RippleArray(1, 2, 3);
|
|
1151
|
+
let $error = null;
|
|
1152
|
+
|
|
1153
|
+
<button onClick={() => {
|
|
1154
|
+
try {
|
|
1155
|
+
items[ARRAY_SET_INDEX_AT](1.5, 10);
|
|
1156
|
+
} catch (e) {
|
|
1157
|
+
$error = e.message;
|
|
1158
|
+
}
|
|
1159
|
+
}}>{'try non-integer'}</button>
|
|
1160
|
+
<button onClick={() => {
|
|
1161
|
+
try {
|
|
1162
|
+
$error = null;
|
|
1163
|
+
items[ARRAY_SET_INDEX_AT](-5, 10);
|
|
1164
|
+
} catch (e) {
|
|
1165
|
+
$error = e.message;
|
|
1166
|
+
}
|
|
1167
|
+
}}>{'try out of bounds negative'}</button>
|
|
1168
|
+
<pre>{$error}</pre>
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
render(ArrayTest);
|
|
1172
|
+
|
|
1173
|
+
const nonIntegerButton = container.querySelectorAll('button')[0];
|
|
1174
|
+
const outOfBoundsButton = container.querySelectorAll('button')[1];
|
|
1175
|
+
|
|
1176
|
+
// Test non-integer index
|
|
1177
|
+
nonIntegerButton.click();
|
|
1178
|
+
flushSync();
|
|
1179
|
+
|
|
1180
|
+
expect(container.querySelector('pre').textContent).toBe('Provided index must be a valid integer');
|
|
1181
|
+
|
|
1182
|
+
// Test out of bounds negative index
|
|
1183
|
+
outOfBoundsButton.click();
|
|
1184
|
+
flushSync();
|
|
1185
|
+
|
|
1186
|
+
expect(container.querySelector('pre').textContent).toBe('Provided negative index out of bounds');
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
it('handles setting $length property and resizing the array', () => {
|
|
1190
|
+
component ArrayTest() {
|
|
1191
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
1192
|
+
|
|
1193
|
+
<button onClick={() => items.$length = 3}>{'truncate'}</button>
|
|
1194
|
+
<button onClick={() => items.$length = 7}>{'expand'}</button>
|
|
1195
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
1196
|
+
<pre>{items.$length}</pre>
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
render(ArrayTest);
|
|
1200
|
+
|
|
1201
|
+
const truncateButton = container.querySelectorAll('button')[0];
|
|
1202
|
+
const expandButton = container.querySelectorAll('button')[1];
|
|
1203
|
+
|
|
1204
|
+
// Initial state
|
|
1205
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
1206
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
|
|
1207
|
+
|
|
1208
|
+
// Test truncating
|
|
1209
|
+
truncateButton.click();
|
|
1210
|
+
flushSync();
|
|
1211
|
+
|
|
1212
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3]');
|
|
1213
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
1214
|
+
|
|
1215
|
+
// Test expanding
|
|
1216
|
+
expandButton.click();
|
|
1217
|
+
flushSync();
|
|
1218
|
+
|
|
1219
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,null,null,null,null]');
|
|
1220
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('7');
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
it('throws error when trying to set length property directly', () => {
|
|
1224
|
+
component ArrayTest() {
|
|
1225
|
+
let items = new RippleArray(1, 2, 3);
|
|
1226
|
+
let $error = null;
|
|
1227
|
+
|
|
1228
|
+
<button onClick={() => {
|
|
1229
|
+
try {
|
|
1230
|
+
items.length = 5;
|
|
1231
|
+
} catch (e) {
|
|
1232
|
+
$error = e.message;
|
|
1233
|
+
}
|
|
1234
|
+
}}>{'try set length'}</button>
|
|
1235
|
+
<pre>{$error}</pre>
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
render(ArrayTest);
|
|
1239
|
+
|
|
1240
|
+
const button = container.querySelector('button');
|
|
1241
|
+
button.click();
|
|
1242
|
+
flushSync();
|
|
1243
|
+
|
|
1244
|
+
expect(container.querySelector('pre').textContent).toBe('Cannot set length on RippleArray, use $length instead');
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
('fromAsync' in Array.prototype ? describe : describe.skip)('RippleArray fromAsync', () => {
|
|
1248
|
+
it('handles static fromAsync method with reactivity', async () => {
|
|
1249
|
+
component ArrayTest() {
|
|
1250
|
+
let itemsPromise = RippleArray.fromAsync(Promise.resolve([1, 2, 3]));
|
|
1251
|
+
let items = null;
|
|
1252
|
+
let error = null;
|
|
1253
|
+
|
|
1254
|
+
try {
|
|
1255
|
+
items = await itemsPromise;
|
|
1256
|
+
} catch (e) {
|
|
1257
|
+
error = e.message;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
<button onClick={() => {
|
|
1261
|
+
if (items) items.push(4);
|
|
1262
|
+
}}>{'add item'}</button>
|
|
1263
|
+
<pre>{error ? 'Error: ' + error : 'Loaded'}</pre>
|
|
1264
|
+
<pre>{items ? JSON.stringify(items) : 'Loading...'}</pre>
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
render(ArrayTest);
|
|
1268
|
+
|
|
1269
|
+
// Wait for promise to resolve
|
|
1270
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1271
|
+
flushSync();
|
|
1272
|
+
|
|
1273
|
+
const addButton = container.querySelector('button');
|
|
1274
|
+
|
|
1275
|
+
// Check that the promise resolved correctly
|
|
1276
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('Loaded');
|
|
1277
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,3]');
|
|
1278
|
+
|
|
1279
|
+
// Test adding an item to the async-created array
|
|
1280
|
+
addButton.click();
|
|
1281
|
+
flushSync();
|
|
1282
|
+
|
|
1283
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,3,4]');
|
|
1284
|
+
});
|
|
1285
|
+
|
|
1286
|
+
it('handles static fromAsync method with mapping function', async () => {
|
|
1287
|
+
component ArrayTest() {
|
|
1288
|
+
let itemsPromise = RippleArray.fromAsync(
|
|
1289
|
+
Promise.resolve([1, 2, 3]),
|
|
1290
|
+
x => x * 2
|
|
1291
|
+
);
|
|
1292
|
+
let items = null;
|
|
1293
|
+
|
|
1294
|
+
items = await itemsPromise;
|
|
1295
|
+
|
|
1296
|
+
<button onClick={() => {
|
|
1297
|
+
if (items) items.push(8);
|
|
1298
|
+
}}>{'add item'}</button>
|
|
1299
|
+
<pre>{items ? JSON.stringify(items) : 'Loading...'}</pre>
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
render(ArrayTest);
|
|
1303
|
+
|
|
1304
|
+
// Wait for promise to resolve
|
|
1305
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1306
|
+
flushSync();
|
|
1307
|
+
|
|
1308
|
+
const addButton = container.querySelector('button');
|
|
1309
|
+
|
|
1310
|
+
// Check that the promise resolved correctly with mapping applied
|
|
1311
|
+
expect(container.querySelector('pre').textContent).toBe('[2,4,6]');
|
|
1312
|
+
|
|
1313
|
+
// Test adding an item to the async-created array
|
|
1314
|
+
addButton.click();
|
|
1315
|
+
flushSync();
|
|
1316
|
+
|
|
1317
|
+
expect(container.querySelector('pre').textContent).toBe('[2,4,6,8]');
|
|
1318
|
+
});
|
|
1319
|
+
|
|
1320
|
+
it('handles error in fromAsync method', async () => {
|
|
1321
|
+
component ArrayTest() {
|
|
1322
|
+
let itemsPromise = RippleArray.fromAsync(Promise.reject(new Error('Async error')));
|
|
1323
|
+
let items = null;
|
|
1324
|
+
let error = null;
|
|
1325
|
+
|
|
1326
|
+
try {
|
|
1327
|
+
items = await itemsPromise;
|
|
1328
|
+
} catch (e) {
|
|
1329
|
+
error = e.message;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
<pre>{error ? 'Error: ' + error : 'No error'}</pre>
|
|
1333
|
+
<pre>{items ? JSON.stringify(items) : 'No items'}</pre>
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
render(ArrayTest);
|
|
1337
|
+
|
|
1338
|
+
// Wait for promise to reject
|
|
1339
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1340
|
+
flushSync();
|
|
1341
|
+
|
|
1342
|
+
// Check that the error was caught correctly
|
|
1343
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('Error: Async error');
|
|
1344
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('No items');
|
|
1345
|
+
});
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
describe('RippleArray copyWithin', () => {
|
|
1349
|
+
it('handles copyWithin operation with reactivity', () => {
|
|
1350
|
+
component ArrayTest() {
|
|
1351
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
1352
|
+
let $firstItem = items[0];
|
|
1353
|
+
let $thirdItem = items[2];
|
|
1354
|
+
let $fourthItem = items[3];
|
|
1355
|
+
|
|
1356
|
+
<button onClick={() => items.copyWithin(0, 3)}>{'copy end to start'}</button>
|
|
1357
|
+
<button onClick={() => items.copyWithin(2, 0, 2)}>{'copy start to middle'}</button>
|
|
1358
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
1359
|
+
<pre>{$firstItem}</pre>
|
|
1360
|
+
<pre>{$thirdItem}</pre>
|
|
1361
|
+
<pre>{$fourthItem}</pre>
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
render(ArrayTest);
|
|
1365
|
+
|
|
1366
|
+
const copyEndToStartButton = container.querySelectorAll('button')[0];
|
|
1367
|
+
const copyStartToMiddleButton = container.querySelectorAll('button')[1];
|
|
1368
|
+
|
|
1369
|
+
// Initial state
|
|
1370
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
1371
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('1');
|
|
1372
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('3');
|
|
1373
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('4');
|
|
1374
|
+
|
|
1375
|
+
// Test copyWithin from end to start
|
|
1376
|
+
copyEndToStartButton.click();
|
|
1377
|
+
flushSync();
|
|
1378
|
+
|
|
1379
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[4,5,3,4,5]');
|
|
1380
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
1381
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('3');
|
|
1382
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('4');
|
|
1383
|
+
|
|
1384
|
+
// Test copyWithin from start to middle
|
|
1385
|
+
copyStartToMiddleButton.click();
|
|
1386
|
+
flushSync();
|
|
1387
|
+
|
|
1388
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[4,5,4,5,5]');
|
|
1389
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
1390
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('4');
|
|
1391
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('5');
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
it('handles copyWithin with negative indexes and reactivity', () => {
|
|
1395
|
+
component ArrayTest() {
|
|
1396
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
1397
|
+
let $secondItem = items[1];
|
|
1398
|
+
let $thirdItem = items[2];
|
|
1399
|
+
|
|
1400
|
+
<button onClick={() => items.copyWithin(-4, -2)}>{'copy with negative indexes'}</button>
|
|
1401
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
1402
|
+
<pre>{$secondItem}</pre>
|
|
1403
|
+
<pre>{$thirdItem}</pre>
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
render(ArrayTest);
|
|
1407
|
+
|
|
1408
|
+
const copyButton = container.querySelector('button');
|
|
1409
|
+
|
|
1410
|
+
// Initial state
|
|
1411
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
1412
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
1413
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('3');
|
|
1414
|
+
|
|
1415
|
+
// Test copyWithin with negative indexes
|
|
1416
|
+
copyButton.click();
|
|
1417
|
+
flushSync();
|
|
1418
|
+
|
|
1419
|
+
// copyWithin(-4, -2) should copy [4,5] to positions [1,2]
|
|
1420
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,4,5,4,5]');
|
|
1421
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('4');
|
|
1422
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('5');
|
|
1423
|
+
});
|
|
1424
|
+
|
|
1425
|
+
it('handles copyWithin with overlapping ranges', () => {
|
|
1426
|
+
component ArrayTest() {
|
|
1427
|
+
let items = new RippleArray(1, 2, 3, 4, 5);
|
|
1428
|
+
let $entries = items.entries();
|
|
1429
|
+
|
|
1430
|
+
<button onClick={() => items.copyWithin(2, 1, 4)}>{'copy with overlap'}</button>
|
|
1431
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
1432
|
+
|
|
1433
|
+
for (const [i, value] of $entries) {
|
|
1434
|
+
<pre>{`items[${i}]: ${value}`}</pre>
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
render(ArrayTest);
|
|
1439
|
+
|
|
1440
|
+
const copyButton = container.querySelector('button');
|
|
1441
|
+
|
|
1442
|
+
// Initial state
|
|
1443
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
|
|
1444
|
+
|
|
1445
|
+
// Test values from reactive bindings
|
|
1446
|
+
for (let i = 0; i < 5; i++) {
|
|
1447
|
+
expect(container.querySelectorAll('pre')[i + 1].textContent).toBe(`items[${i}]: ${i + 1}`);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// Test copyWithin with overlapping ranges
|
|
1451
|
+
copyButton.click();
|
|
1452
|
+
flushSync();
|
|
1453
|
+
|
|
1454
|
+
// copyWithin(2, 1, 4) should copy [2,3,4] to positions [2,3,4]
|
|
1455
|
+
// resulting in [1,2,2,3,4]
|
|
1456
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,2,3,4]');
|
|
1457
|
+
|
|
1458
|
+
// Test that reactive bindings updated
|
|
1459
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('items[0]: 1');
|
|
1460
|
+
expect(container.querySelectorAll('pre')[2].textContent).toBe('items[1]: 2');
|
|
1461
|
+
expect(container.querySelectorAll('pre')[3].textContent).toBe('items[2]: 2');
|
|
1462
|
+
expect(container.querySelectorAll('pre')[4].textContent).toBe('items[3]: 3');
|
|
1463
|
+
expect(container.querySelectorAll('pre')[5].textContent).toBe('items[4]: 4');
|
|
1464
|
+
});
|
|
1465
|
+
});
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
|