signalium 0.1.0

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.
@@ -0,0 +1,378 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { state, computed } from './utils/instrumented.js';
3
+
4
+ describe.skip('Basic Signal functionality', () => {
5
+ test('Can run basic computed', () => {
6
+ const a = state(1);
7
+ const b = state(2);
8
+
9
+ const c = computed(() => {
10
+ return a.get() + b.get();
11
+ });
12
+
13
+ expect(c).toHaveValueAndCounts(3, { compute: 1, get: 1 });
14
+
15
+ // stability
16
+ expect(c).toHaveValueAndCounts(3, { compute: 1, get: 2 });
17
+ });
18
+
19
+ test('Computeds can be updated', () => {
20
+ const a = state(1);
21
+ const b = state(2);
22
+
23
+ const c = computed(() => {
24
+ return a.get() + b.get();
25
+ });
26
+
27
+ expect(c).toHaveValueAndCounts(3, { compute: 1, get: 1 });
28
+
29
+ a.set(2);
30
+
31
+ expect(c).toHaveValueAndCounts(4, { compute: 2, get: 2 });
32
+ });
33
+
34
+ test('Does not update if value is the same', () => {
35
+ const a = state(1);
36
+ const b = state(2);
37
+
38
+ const c = computed(() => {
39
+ return a.get() + b.get();
40
+ });
41
+
42
+ expect(c).toHaveValueAndCounts(3, { compute: 1, get: 1 });
43
+
44
+ a.set(1);
45
+
46
+ expect(c).toHaveValueAndCounts(3, { compute: 1, get: 2 });
47
+ });
48
+
49
+ describe('Nesting', () => {
50
+ test('Can nest computeds', () => {
51
+ const a = state(1);
52
+ const b = state(2);
53
+ const c = state(2);
54
+
55
+ const inner = computed(() => {
56
+ return a.get() + b.get();
57
+ });
58
+
59
+ const outer = computed(() => {
60
+ return inner.get() + c.get();
61
+ });
62
+
63
+ expect(inner).toHaveValueAndCounts(3, { compute: 1, get: 1 });
64
+ expect(outer).toHaveValueAndCounts(5, { compute: 1, get: 1 });
65
+
66
+ // stability
67
+ expect(inner).toHaveValueAndCounts(3, { compute: 1, get: 3 });
68
+ expect(outer).toHaveValueAndCounts(5, { compute: 1, get: 2 });
69
+ });
70
+
71
+ test('Can dirty inner computed and update parent', () => {
72
+ const a = state(1);
73
+ const b = state(2);
74
+ const c = state(2);
75
+
76
+ const inner = computed(() => {
77
+ return a.get() + b.get();
78
+ });
79
+
80
+ const outer = computed(() => {
81
+ return inner.get() + c.get();
82
+ });
83
+
84
+ expect(inner).toHaveValueAndCounts(3, { compute: 1, get: 1 });
85
+ expect(outer).toHaveValueAndCounts(5, { compute: 1, get: 1 });
86
+
87
+ a.set(2);
88
+
89
+ expect(inner).toHaveValueAndCounts(4, { compute: 2, get: 3 });
90
+ expect(outer).toHaveValueAndCounts(6, { compute: 2, get: 2 });
91
+ });
92
+
93
+ test('Can dirty outer computed and inner stays cached', () => {
94
+ const a = state(1);
95
+ const b = state(2);
96
+ const c = state(2);
97
+
98
+ const inner = computed(() => {
99
+ return a.get() + b.get();
100
+ });
101
+
102
+ const outer = computed(() => {
103
+ return inner.get() + c.get();
104
+ });
105
+
106
+ expect(inner).toHaveValueAndCounts(3, { compute: 1, get: 1 });
107
+ expect(outer).toHaveValueAndCounts(5, { compute: 1, get: 1 });
108
+
109
+ c.set(3);
110
+
111
+ expect(inner).toHaveValueAndCounts(3, { compute: 1, get: 3 });
112
+ expect(outer).toHaveValueAndCounts(6, { compute: 2, get: 2 });
113
+ });
114
+
115
+ test('Can nest multiple levels', () => {
116
+ const a = state(1);
117
+ const b = state(2);
118
+ const c = state(2);
119
+ const d = state(2);
120
+
121
+ const inner = computed(() => {
122
+ return a.get() + b.get();
123
+ });
124
+
125
+ const mid = computed(() => {
126
+ return inner.get() + c.get();
127
+ });
128
+
129
+ const outer = computed(() => {
130
+ return mid.get() + d.get();
131
+ });
132
+
133
+ expect(inner).toHaveValueAndCounts(3, { compute: 1 });
134
+ expect(mid).toHaveValueAndCounts(5, { compute: 1 });
135
+ expect(outer).toHaveValueAndCounts(7, { compute: 1 });
136
+
137
+ a.set(2);
138
+
139
+ expect(inner).toHaveValueAndCounts(4, { compute: 2 });
140
+ expect(mid).toHaveValueAndCounts(6, { compute: 2 });
141
+ expect(outer).toHaveValueAndCounts(8, { compute: 2 });
142
+
143
+ c.set(3);
144
+
145
+ expect(inner).toHaveValueAndCounts(4, { compute: 2 });
146
+ expect(mid).toHaveValueAndCounts(7, { compute: 3 });
147
+ expect(outer).toHaveValueAndCounts(9, { compute: 3 });
148
+
149
+ d.set(3);
150
+
151
+ expect(inner).toHaveValueAndCounts(4, { compute: 2 });
152
+ expect(mid).toHaveValueAndCounts(7, { compute: 3 });
153
+ expect(outer).toHaveValueAndCounts(10, { compute: 4 });
154
+ });
155
+ });
156
+
157
+ describe('Propagation', () => {
158
+ test('it works with multiple parents', () => {
159
+ const a = state(1);
160
+ const b = state(2);
161
+ const c = state(2);
162
+ const d = state(2);
163
+
164
+ const inner = computed(() => {
165
+ return a.get() + b.get();
166
+ });
167
+
168
+ const outer1 = computed(() => {
169
+ return inner.get() + c.get();
170
+ });
171
+
172
+ const outer2 = computed(() => {
173
+ return inner.get() + d.get();
174
+ });
175
+
176
+ expect(inner).toHaveValueAndCounts(3, { compute: 1 });
177
+ expect(outer1).toHaveValueAndCounts(5, { compute: 1 });
178
+ expect(outer2).toHaveValueAndCounts(5, { compute: 1 });
179
+
180
+ a.set(2);
181
+
182
+ expect(inner).toHaveValueAndCounts(4, { compute: 2 });
183
+ expect(outer1).toHaveValueAndCounts(6, { compute: 2 });
184
+ expect(outer2).toHaveValueAndCounts(6, { compute: 2 });
185
+
186
+ b.set(3);
187
+
188
+ expect(inner).toHaveValueAndCounts(5, { compute: 3 });
189
+ expect(outer2).toHaveValueAndCounts(7, { compute: 3 });
190
+ expect(outer1).toHaveValueAndCounts(7, { compute: 3 });
191
+
192
+ c.set(3);
193
+
194
+ expect(inner).toHaveValueAndCounts(5, { compute: 3 });
195
+ expect(outer1).toHaveValueAndCounts(8, { compute: 4 });
196
+ expect(outer2).toHaveValueAndCounts(7, { compute: 3 });
197
+
198
+ d.set(3);
199
+
200
+ expect(inner).toHaveValueAndCounts(5, { compute: 3 });
201
+ expect(outer1).toHaveValueAndCounts(8, { compute: 4 });
202
+ expect(outer2).toHaveValueAndCounts(8, { compute: 4 });
203
+ });
204
+
205
+ test('it stops propagation if the result is the same', () => {
206
+ const a = state(1);
207
+ const b = state(2);
208
+ const c = state(2);
209
+ const d = state(2);
210
+
211
+ const inner = computed('inner', () => {
212
+ return a.get() + b.get();
213
+ });
214
+
215
+ const outer1 = computed('outer1', () => {
216
+ return inner.get() + c.get();
217
+ });
218
+
219
+ const outer2 = computed('outer2', () => {
220
+ return inner.get() + d.get();
221
+ });
222
+
223
+ expect(() => {
224
+ expect(outer1).toHaveValueAndCounts(5, { compute: 1 });
225
+ expect(outer2).toHaveValueAndCounts(5, { compute: 1 });
226
+ expect(inner).toHaveValueAndCounts(3, { compute: 1 });
227
+ }).toHaveComputedOrder(['outer1', 'inner', 'outer2']);
228
+
229
+ a.set(2);
230
+ b.set(1);
231
+
232
+ expect(() => {
233
+ expect(outer1).toHaveValueAndCounts(5, { compute: 1 });
234
+ expect(outer2).toHaveValueAndCounts(5, { compute: 1 });
235
+ expect(inner).toHaveValueAndCounts(3, { compute: 2 });
236
+ }).toHaveComputedOrder(['inner']);
237
+
238
+ b.set(2);
239
+
240
+ expect(() => {
241
+ expect(outer2).toHaveValueAndCounts(6, { compute: 2 });
242
+ expect(outer1).toHaveValueAndCounts(6, { compute: 2 });
243
+ expect(inner).toHaveValueAndCounts(4, { compute: 3 });
244
+ }).toHaveComputedOrder(['inner', 'outer2', 'outer1']);
245
+ });
246
+
247
+ test('it continues propagation if any child is different', () => {
248
+ const a = state(1);
249
+ const b = state(2);
250
+ const c = state(2);
251
+ const d = state(2);
252
+
253
+ const inner1 = computed('inner1', () => {
254
+ return a.get() + b.get();
255
+ });
256
+
257
+ const inner2 = computed('inner2', () => {
258
+ return c.get();
259
+ });
260
+
261
+ const inner3 = computed('inner3', () => {
262
+ return d.get();
263
+ });
264
+
265
+ const outer = computed('outer', () => {
266
+ return inner1.get() + inner2.get() + inner3.get();
267
+ });
268
+
269
+ expect(() => {
270
+ expect(outer).toHaveValueAndCounts(7, { compute: 1 });
271
+ }).toHaveComputedOrder(['outer', 'inner1', 'inner2', 'inner3']);
272
+
273
+ d.set(4);
274
+ a.set(2);
275
+ c.set(3);
276
+ b.set(1);
277
+
278
+ expect(() => {
279
+ expect(outer).toHaveValueAndCounts(10, { compute: 2 });
280
+ expect(inner1).toHaveCounts({ compute: 2 });
281
+ expect(inner2).toHaveCounts({ compute: 2 });
282
+ }).toHaveComputedOrder(['inner1', 'inner2', 'outer', 'inner3']);
283
+ });
284
+ });
285
+
286
+ describe('Laziness', () => {
287
+ test('it does not compute values that are not used', () => {
288
+ const a = state(1);
289
+ const b = state(2);
290
+ const c = state(2);
291
+ const d = state(2);
292
+
293
+ const inner1 = computed(() => {
294
+ return a.get() + b.get();
295
+ });
296
+
297
+ const inner2 = computed(() => {
298
+ return c.get() + d.get();
299
+ });
300
+
301
+ const outer = computed(() => {
302
+ if (inner1.get() <= 3) {
303
+ return inner2.get();
304
+ } else {
305
+ return -1;
306
+ }
307
+ });
308
+
309
+ expect(outer).toHaveValueAndCounts(4, { compute: 1 });
310
+
311
+ a.set(2);
312
+ c.set(3);
313
+
314
+ expect(outer).toHaveValueAndCounts(-1, { compute: 2 });
315
+ expect(inner1).toHaveCounts({ compute: 2 });
316
+ expect(inner2).toHaveCounts({ compute: 1 });
317
+ });
318
+ });
319
+
320
+ describe('Equality', () => {
321
+ test('Does not update if value is the same (custom equality fn)', () => {
322
+ const a = state(1);
323
+ const b = state(2);
324
+
325
+ const c = computed(
326
+ () => {
327
+ return a.get() + b.get();
328
+ },
329
+ {
330
+ equals(prev, next) {
331
+ return Math.abs(prev - next) < 2;
332
+ },
333
+ },
334
+ );
335
+
336
+ expect(c).toHaveValueAndCounts(3, { compute: 1 });
337
+
338
+ a.set(2);
339
+
340
+ expect(c).toHaveValueAndCounts(3, { compute: 2 });
341
+ });
342
+
343
+ test('It stops propagation if the result is the same (custom equality fn)', () => {
344
+ const a = state(1);
345
+ const b = state(2);
346
+ const c = state(2);
347
+ const d = state(2);
348
+
349
+ const inner = computed(
350
+ () => {
351
+ return a.get() + b.get();
352
+ },
353
+ {
354
+ equals(prev, next) {
355
+ return Math.abs(prev - next) < 2;
356
+ },
357
+ },
358
+ );
359
+
360
+ const outer1 = computed(() => {
361
+ return inner.get() + c.get();
362
+ });
363
+
364
+ const outer2 = computed(() => {
365
+ return inner.get() + d.get();
366
+ });
367
+
368
+ expect(inner).toHaveValueAndCounts(3, { compute: 1 });
369
+
370
+ a.set(2);
371
+ b.set(2);
372
+
373
+ expect(inner).toHaveValueAndCounts(3, { compute: 2 });
374
+ expect(outer1).toHaveValueAndCounts(5, { compute: 1 });
375
+ expect(outer2).toHaveValueAndCounts(5, { compute: 1 });
376
+ });
377
+ });
378
+ });