@zeix/cause-effect 0.9.7
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/.editorconfig +7 -0
- package/LICENSE +21 -0
- package/README.md +122 -0
- package/index.d.ts +9 -0
- package/index.js +1 -0
- package/index.ts +9 -0
- package/lib/computed.d.ts +14 -0
- package/lib/computed.ts +75 -0
- package/lib/effect.d.ts +7 -0
- package/lib/effect.ts +21 -0
- package/lib/signal.d.ts +48 -0
- package/lib/signal.ts +98 -0
- package/lib/state.d.ts +38 -0
- package/lib/state.ts +67 -0
- package/lib/util.d.ts +6 -0
- package/lib/util.ts +16 -0
- package/package.json +30 -0
- package/test/benchmark.test.ts +507 -0
- package/test/cause-effect.test.ts +467 -0
- package/test/util/dependency-graph.ts +131 -0
- package/test/util/pseudo-random.ts +45 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test'
|
|
2
|
+
import { state, computed, effect, batch } from '../index';
|
|
3
|
+
import { makeGraph, runGraph, Counter } from "./util/dependency-graph";
|
|
4
|
+
|
|
5
|
+
/* === Utility Functions === */
|
|
6
|
+
|
|
7
|
+
const busy = () => {
|
|
8
|
+
let a = 0;
|
|
9
|
+
for (let i = 0; i < 1_00; i++) {
|
|
10
|
+
a++;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const framework = {
|
|
15
|
+
name: "Cause & Effect",
|
|
16
|
+
signal: <T>(initialValue: T) => {
|
|
17
|
+
const s = state<T>(initialValue);
|
|
18
|
+
return {
|
|
19
|
+
write: (v: T) => s.set(v),
|
|
20
|
+
read: () => s.get(),
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
computed: <T>(fn: () => T) => {
|
|
24
|
+
const c = computed(fn, true);
|
|
25
|
+
return { read: () => c.get() };
|
|
26
|
+
},
|
|
27
|
+
effect: (fn: () => void) => effect(fn),
|
|
28
|
+
withBatch: (fn: () => void) => batch(fn),
|
|
29
|
+
withBuild: (fn: () => void) => fn(),
|
|
30
|
+
};
|
|
31
|
+
const testPullCounts = true;
|
|
32
|
+
|
|
33
|
+
function makeConfig() {
|
|
34
|
+
return {
|
|
35
|
+
width: 3,
|
|
36
|
+
totalLayers: 3,
|
|
37
|
+
staticFraction: 1,
|
|
38
|
+
nSources: 2,
|
|
39
|
+
readFraction: 1,
|
|
40
|
+
expected: {},
|
|
41
|
+
iterations: 1,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* === Test functions === */
|
|
46
|
+
|
|
47
|
+
/** some basic tests to validate the reactive framework
|
|
48
|
+
* wrapper works and can run performance tests.
|
|
49
|
+
*/
|
|
50
|
+
describe('Basic test', function () {
|
|
51
|
+
const name = framework.name;
|
|
52
|
+
|
|
53
|
+
test(`${name} | simple dependency executes`, () => {
|
|
54
|
+
const s = framework.signal(2);
|
|
55
|
+
const c = framework.computed(() => s.read()! * 2);
|
|
56
|
+
|
|
57
|
+
expect(c.read()).toBe(4);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test(`${name} | static graph`, () => {
|
|
61
|
+
const config = makeConfig();
|
|
62
|
+
const { graph, counter } = makeGraph(framework, config);
|
|
63
|
+
const sum = runGraph(graph, 2, 1, framework);
|
|
64
|
+
|
|
65
|
+
expect(sum).toBe(16);
|
|
66
|
+
if (testPullCounts) {
|
|
67
|
+
expect(counter.count).toBe(11);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test(`${name} | static graph, read 2/3 of leaves`, () => {
|
|
72
|
+
const config = makeConfig();
|
|
73
|
+
config.readFraction = 2 / 3;
|
|
74
|
+
config.iterations = 10;
|
|
75
|
+
const { counter, graph } = makeGraph(framework, config);
|
|
76
|
+
const sum = runGraph(graph, 10, 2 / 3, framework);
|
|
77
|
+
|
|
78
|
+
expect(sum).toBe(72);
|
|
79
|
+
if (testPullCounts) {
|
|
80
|
+
expect(counter.count).toBe(41);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test(`${name} | dynamic graph`, () => {
|
|
85
|
+
const config = makeConfig();
|
|
86
|
+
config.staticFraction = 0.5;
|
|
87
|
+
config.width = 4;
|
|
88
|
+
config.totalLayers = 2;
|
|
89
|
+
const { graph, counter } = makeGraph(framework, config);
|
|
90
|
+
const sum = runGraph(graph, 10, 1, framework);
|
|
91
|
+
|
|
92
|
+
expect(sum).toBe(72);
|
|
93
|
+
if (testPullCounts) {
|
|
94
|
+
expect(counter.count).toBe(22);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('Kairo tests', function () {
|
|
100
|
+
const name = framework.name;
|
|
101
|
+
|
|
102
|
+
test(`${name} | avoidable propagation`, () => {
|
|
103
|
+
const head = framework.signal(0);
|
|
104
|
+
const computed1 = framework.computed(() => head.read());
|
|
105
|
+
const computed2 = framework.computed(() => (computed1.read(), 0));
|
|
106
|
+
const computed3 = framework.computed(() => (busy(), computed2.read()! + 1)); // heavy computation
|
|
107
|
+
const computed4 = framework.computed(() => computed3.read()! + 2);
|
|
108
|
+
const computed5 = framework.computed(() => computed4.read()! + 3);
|
|
109
|
+
framework.effect(() => {
|
|
110
|
+
computed5.read();
|
|
111
|
+
busy(); // heavy side effect
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
framework.withBatch(() => {
|
|
116
|
+
head.write(1);
|
|
117
|
+
});
|
|
118
|
+
expect(computed5.read()).toBe(6);
|
|
119
|
+
for (let i = 0; i < 10; i++) {
|
|
120
|
+
framework.withBatch(() => {
|
|
121
|
+
head.write(i);
|
|
122
|
+
});
|
|
123
|
+
expect(computed5.read()).toBe(6);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test(`${name} | broad propagation`, () => {
|
|
129
|
+
const head = framework.signal(0);
|
|
130
|
+
let last = head as { read: () => number };
|
|
131
|
+
const callCounter = new Counter();
|
|
132
|
+
for (let i = 0; i < 50; i++) {
|
|
133
|
+
let current = framework.computed(() => {
|
|
134
|
+
return head.read()! + i;
|
|
135
|
+
});
|
|
136
|
+
let current2 = framework.computed(() => {
|
|
137
|
+
return current.read()! + 1;
|
|
138
|
+
});
|
|
139
|
+
framework.effect(() => {
|
|
140
|
+
current2.read();
|
|
141
|
+
callCounter.count++;
|
|
142
|
+
});
|
|
143
|
+
last = current2;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return () => {
|
|
147
|
+
framework.withBatch(() => {
|
|
148
|
+
head.write(1);
|
|
149
|
+
});
|
|
150
|
+
const atleast = 50 * 50;
|
|
151
|
+
callCounter.count = 0;
|
|
152
|
+
for (let i = 0; i < 50; i++) {
|
|
153
|
+
framework.withBatch(() => {
|
|
154
|
+
head.write(i);
|
|
155
|
+
});
|
|
156
|
+
expect(last.read()).toBe(i + 50);
|
|
157
|
+
}
|
|
158
|
+
expect(callCounter.count).toBe(atleast);
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test(`${name} | deep propagation`, () => {
|
|
163
|
+
let len = 50;
|
|
164
|
+
const head = framework.signal(0);
|
|
165
|
+
let current = head as { read: () => number };
|
|
166
|
+
for (let i = 0; i < len; i++) {
|
|
167
|
+
let c = current;
|
|
168
|
+
current = framework.computed(() => {
|
|
169
|
+
return c.read() + 1;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
let callCounter = new Counter();
|
|
173
|
+
framework.effect(() => {
|
|
174
|
+
current.read();
|
|
175
|
+
callCounter.count++;
|
|
176
|
+
});
|
|
177
|
+
const iter = 50;
|
|
178
|
+
|
|
179
|
+
return () => {
|
|
180
|
+
framework.withBatch(() => {
|
|
181
|
+
head.write(1);
|
|
182
|
+
});
|
|
183
|
+
const atleast = iter;
|
|
184
|
+
callCounter.count = 0;
|
|
185
|
+
for (let i = 0; i < iter; i++) {
|
|
186
|
+
framework.withBatch(() => {
|
|
187
|
+
head.write(i);
|
|
188
|
+
});
|
|
189
|
+
expect(current.read()).toBe(len + i);
|
|
190
|
+
}
|
|
191
|
+
expect(callCounter.count).toBe(atleast);
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test(`${name} | diamond`, function () {
|
|
196
|
+
let width = 5;
|
|
197
|
+
const head = framework.signal(0);
|
|
198
|
+
let current: { read(): number }[] = [];
|
|
199
|
+
for (let i = 0; i < width; i++) {
|
|
200
|
+
current.push(
|
|
201
|
+
framework.computed(() => head.read() + 1)
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
let sum = framework.computed(() => {
|
|
205
|
+
return current.map(x => x.read()).reduce((a, b) => a + b, 0);
|
|
206
|
+
});
|
|
207
|
+
let callCounter = new Counter();
|
|
208
|
+
framework.effect(() => {
|
|
209
|
+
sum.read();
|
|
210
|
+
callCounter.count++;
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return () => {
|
|
214
|
+
framework.withBatch(() => {
|
|
215
|
+
head.write(1);
|
|
216
|
+
});
|
|
217
|
+
expect(sum.read()).toBe(2 * width);
|
|
218
|
+
const atleast = 500;
|
|
219
|
+
callCounter.count = 0;
|
|
220
|
+
for (let i = 0; i < 500; i++) {
|
|
221
|
+
framework.withBatch(() => {
|
|
222
|
+
head.write(i);
|
|
223
|
+
});
|
|
224
|
+
expect(sum.read()).toBe((i + 1) * width);
|
|
225
|
+
}
|
|
226
|
+
expect(callCounter.count).toBe(atleast);
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test(`${name} | mux`, function () {
|
|
231
|
+
let heads = new Array(100).fill(null).map((_) => framework.signal(0));
|
|
232
|
+
const mux = framework.computed(() => {
|
|
233
|
+
return Object.fromEntries(heads.map((h) => h.read()).entries());
|
|
234
|
+
});
|
|
235
|
+
const splited = heads
|
|
236
|
+
.map((_, index) => framework.computed(() => mux.read()[index]))
|
|
237
|
+
.map((x) => framework.computed(() => x.read() + 1));
|
|
238
|
+
|
|
239
|
+
splited.forEach((x) => {
|
|
240
|
+
framework.effect(() => x.read());
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return () => {
|
|
244
|
+
for (let i = 0; i < 10; i++) {
|
|
245
|
+
framework.withBatch(() => {
|
|
246
|
+
heads[i].write(i);
|
|
247
|
+
});
|
|
248
|
+
expect(splited[i].read()).toBe(i + 1);
|
|
249
|
+
}
|
|
250
|
+
for (let i = 0; i < 10; i++) {
|
|
251
|
+
framework.withBatch(() => {
|
|
252
|
+
heads[i].write(i * 2);
|
|
253
|
+
});
|
|
254
|
+
expect(splited[i].read()).toBe(i * 2 + 1);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test(`${name} | repeated observers`, function () {
|
|
260
|
+
const size = 30;
|
|
261
|
+
let head = framework.signal(0);
|
|
262
|
+
let current = framework.computed(() => {
|
|
263
|
+
let result = 0;
|
|
264
|
+
for (let i = 0; i < size; i++) {
|
|
265
|
+
result += head.read();
|
|
266
|
+
}
|
|
267
|
+
return result;
|
|
268
|
+
});
|
|
269
|
+
let callCounter = new Counter();
|
|
270
|
+
framework.effect(() => {
|
|
271
|
+
current.read();
|
|
272
|
+
callCounter.count++;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return () => {
|
|
276
|
+
framework.withBatch(() => {
|
|
277
|
+
head.write(1);
|
|
278
|
+
});
|
|
279
|
+
expect(current.read()).toBe(size);
|
|
280
|
+
const atleast = 100;
|
|
281
|
+
callCounter.count = 0;
|
|
282
|
+
for (let i = 0; i < 100; i++) {
|
|
283
|
+
framework.withBatch(() => {
|
|
284
|
+
head.write(i);
|
|
285
|
+
});
|
|
286
|
+
expect(current.read()).toBe(i * size);
|
|
287
|
+
}
|
|
288
|
+
expect(callCounter.count).toBe(atleast);
|
|
289
|
+
};
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test(`${name} | triangle`, function () {
|
|
293
|
+
let width = 10;
|
|
294
|
+
let head = framework.signal(0);
|
|
295
|
+
let current = head as { read: () => number };
|
|
296
|
+
let list: { read: () => number }[] = [];
|
|
297
|
+
for (let i = 0; i < width; i++) {
|
|
298
|
+
let c = current;
|
|
299
|
+
list.push(current);
|
|
300
|
+
current = framework.computed(() => {
|
|
301
|
+
return c.read() + 1;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
let sum = framework.computed(() => {
|
|
305
|
+
return list.map((x) => x.read()).reduce((a, b) => a + b, 0);
|
|
306
|
+
});
|
|
307
|
+
let callCounter = new Counter();
|
|
308
|
+
framework.effect(() => {
|
|
309
|
+
sum.read();
|
|
310
|
+
callCounter.count++;
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
return () => {
|
|
314
|
+
const count = (number: Number) => {
|
|
315
|
+
return new Array(number)
|
|
316
|
+
.fill(0)
|
|
317
|
+
.map((_, i) => i + 1)
|
|
318
|
+
.reduce((x, y) => x + y, 0);
|
|
319
|
+
}
|
|
320
|
+
const constant = count(width);
|
|
321
|
+
framework.withBatch(() => {
|
|
322
|
+
head.write(1);
|
|
323
|
+
});
|
|
324
|
+
expect(sum.read()).toBe(constant);
|
|
325
|
+
const atleast = 100;
|
|
326
|
+
callCounter.count = 0;
|
|
327
|
+
for (let i = 0; i < 100; i++) {
|
|
328
|
+
framework.withBatch(() => {
|
|
329
|
+
head.write(i);
|
|
330
|
+
});
|
|
331
|
+
expect(sum.read()).toBe(constant - width + i * width);
|
|
332
|
+
}
|
|
333
|
+
expect(callCounter.count).toBe(atleast);
|
|
334
|
+
};
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test(`${name} | unstable`, function () {
|
|
338
|
+
let head = framework.signal(0);
|
|
339
|
+
const double = framework.computed(() => head.read() * 2);
|
|
340
|
+
const inverse = framework.computed(() => -head.read());
|
|
341
|
+
let current = framework.computed(() => {
|
|
342
|
+
let result = 0;
|
|
343
|
+
for (let i = 0; i < 20; i++) {
|
|
344
|
+
result += head.read() % 2 ? double.read() : inverse.read();
|
|
345
|
+
}
|
|
346
|
+
return result;
|
|
347
|
+
});
|
|
348
|
+
let callCounter = new Counter();
|
|
349
|
+
framework.effect(() => {
|
|
350
|
+
current.read();
|
|
351
|
+
callCounter.count++;
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
return () => {
|
|
355
|
+
framework.withBatch(() => {
|
|
356
|
+
head.write(1);
|
|
357
|
+
});
|
|
358
|
+
expect(current.read()).toBe(40);
|
|
359
|
+
const atleast = 100;
|
|
360
|
+
callCounter.count = 0;
|
|
361
|
+
for (let i = 0; i < 100; i++) {
|
|
362
|
+
framework.withBatch(() => {
|
|
363
|
+
head.write(i);
|
|
364
|
+
});
|
|
365
|
+
expect(current.read()).toBe(i % 2 ? i * 2 * 10 : i * -10);
|
|
366
|
+
}
|
|
367
|
+
expect(callCounter.count).toBe(atleast);
|
|
368
|
+
};
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
/* describe('$mol_wire tests', function () {
|
|
373
|
+
const name = framework.name;
|
|
374
|
+
|
|
375
|
+
test(`${name} | $mol_wire benchmark`, function() {
|
|
376
|
+
const fib = (n: number) => {
|
|
377
|
+
if (n < 2) return 1;
|
|
378
|
+
return fib(n - 1) + fib(n - 2);
|
|
379
|
+
};
|
|
380
|
+
const hard = (n: number, log: string) => {
|
|
381
|
+
return n + fib(16);
|
|
382
|
+
};
|
|
383
|
+
const numbers = Array.from({ length: 5 }, (_, i) => i);
|
|
384
|
+
let res: (() => any)[] = [];
|
|
385
|
+
const iter = framework.withBuild(() => {
|
|
386
|
+
const A = framework.signal(0);
|
|
387
|
+
const B = framework.signal(0);
|
|
388
|
+
const C = framework.computed(() => (A.read() % 2) + (B.read() % 2));
|
|
389
|
+
const D = framework.computed(() =>
|
|
390
|
+
numbers.map((i) => ({ x: i + (A.read() % 2) - (B.read() % 2) }))
|
|
391
|
+
);
|
|
392
|
+
const E = framework.computed(() =>
|
|
393
|
+
hard(C.read() + A.read() + D.read()[0].x, "E")
|
|
394
|
+
);
|
|
395
|
+
const F = framework.computed(() => hard(D.read()[2].x || B.read(), "F"));
|
|
396
|
+
const G = framework.computed(
|
|
397
|
+
() => C.read() + (C.read() || E.read() % 2) + D.read()[4].x + F.read()
|
|
398
|
+
);
|
|
399
|
+
framework.effect(() => res.push(hard(G.read(), "H")));
|
|
400
|
+
framework.effect(() => res.push(G.read())); // I
|
|
401
|
+
framework.effect(() => res.push(hard(F.read(), "J")));
|
|
402
|
+
framework.effect(() => res[0] = hard(G.read(), "H"));
|
|
403
|
+
framework.effect(() => res[1] = G.read()); // I
|
|
404
|
+
framework.effect(() => res[2] = hard(F.read(), "J"));
|
|
405
|
+
|
|
406
|
+
return (i: number) => {
|
|
407
|
+
res.length = 0;
|
|
408
|
+
framework.withBatch(() => {
|
|
409
|
+
B.write(1);
|
|
410
|
+
A.write(1 + i * 2);
|
|
411
|
+
});
|
|
412
|
+
framework.withBatch(() => {
|
|
413
|
+
A.write(2 + i * 2);
|
|
414
|
+
B.write(2);
|
|
415
|
+
});
|
|
416
|
+
};
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
expect(res.toString()).toBe([3201, 1604, 3196].toString());
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
describe('CellX tests', function () {
|
|
424
|
+
const name = framework.name;
|
|
425
|
+
|
|
426
|
+
test(`${name} | CellX benchmark`, function() {
|
|
427
|
+
const expected = {
|
|
428
|
+
1000: [
|
|
429
|
+
[-3, -6, -2, 2],
|
|
430
|
+
[-2, -4, 2, 3],
|
|
431
|
+
],
|
|
432
|
+
2500: [
|
|
433
|
+
[-3, -6, -2, 2],
|
|
434
|
+
[-2, -4, 2, 3],
|
|
435
|
+
],
|
|
436
|
+
5000: [
|
|
437
|
+
[2, 4, -1, -6],
|
|
438
|
+
[-2, 1, -4, -4],
|
|
439
|
+
]
|
|
440
|
+
};
|
|
441
|
+
const results = {};
|
|
442
|
+
|
|
443
|
+
const cellx = (framework, layers) => {
|
|
444
|
+
const start = {
|
|
445
|
+
prop1: framework.signal(1),
|
|
446
|
+
prop2: framework.signal(2),
|
|
447
|
+
prop3: framework.signal(3),
|
|
448
|
+
prop4: framework.signal(4),
|
|
449
|
+
};
|
|
450
|
+
let layer = start;
|
|
451
|
+
|
|
452
|
+
for (let i = layers; i > 0; i--) {
|
|
453
|
+
const m = layer;
|
|
454
|
+
const s = {
|
|
455
|
+
prop1: framework.computed(() => m.prop2.read()),
|
|
456
|
+
prop2: framework.computed(() => m.prop1.read() - m.prop3.read()),
|
|
457
|
+
prop3: framework.computed(() => m.prop2.read() + m.prop4.read()),
|
|
458
|
+
prop4: framework.computed(() => m.prop3.read()),
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
framework.effect(() => s.prop1.read());
|
|
462
|
+
framework.effect(() => s.prop2.read());
|
|
463
|
+
framework.effect(() => s.prop3.read());
|
|
464
|
+
framework.effect(() => s.prop4.read());
|
|
465
|
+
|
|
466
|
+
s.prop1.read();
|
|
467
|
+
s.prop2.read();
|
|
468
|
+
s.prop3.read();
|
|
469
|
+
s.prop4.read();
|
|
470
|
+
|
|
471
|
+
layer = s;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const end = layer;
|
|
475
|
+
|
|
476
|
+
const before = [
|
|
477
|
+
end.prop1.read(),
|
|
478
|
+
end.prop2.read(),
|
|
479
|
+
end.prop3.read(),
|
|
480
|
+
end.prop4.read(),
|
|
481
|
+
];
|
|
482
|
+
|
|
483
|
+
framework.withBatch(() => {
|
|
484
|
+
start.prop1.write(4);
|
|
485
|
+
start.prop2.write(3);
|
|
486
|
+
start.prop3.write(2);
|
|
487
|
+
start.prop4.write(1);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
const after = [
|
|
491
|
+
end.prop1.read(),
|
|
492
|
+
end.prop2.read(),
|
|
493
|
+
end.prop3.read(),
|
|
494
|
+
end.prop4.read(),
|
|
495
|
+
];
|
|
496
|
+
|
|
497
|
+
return [before, after];
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
for (const layers in expected) {
|
|
501
|
+
const [before, after] = cellx(framework, layers);
|
|
502
|
+
const [expectedBefore, expectedAfter] = expected[layers];
|
|
503
|
+
expect(before.toString()).toBe(expectedBefore.toString());
|
|
504
|
+
expect(after.toString()).toBe(expectedAfter.toString());
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
}); */
|