136 lines
3.0 KiB
JavaScript
136 lines
3.0 KiB
JavaScript
import { prop, computed, stream, hashableStream } from '../src/index.js';
|
|
import { dirtyMock, hashSet } from '../src/testUtil.js';
|
|
|
|
describe('A stream', () => {
|
|
const add = (a, b) => a + b;
|
|
const square = a => a * a;
|
|
|
|
async function delayAdd(a, b) {
|
|
return new Promise(resolve =>
|
|
setTimeout(() => {
|
|
resolve(a + b);
|
|
}, 30)
|
|
);
|
|
}
|
|
async function delaySquare(a) {
|
|
return new Promise(resolve =>
|
|
setTimeout(() => {
|
|
resolve(a * a);
|
|
}, 30)
|
|
);
|
|
}
|
|
|
|
it('accepts subscribable dependencies', async done => {
|
|
const a = prop(0);
|
|
const b = computed(square, [a]);
|
|
const c = stream(delaySquare, [a]);
|
|
const d = stream(delayAdd, [a, c]);
|
|
const e = stream(delaySquare, [b]);
|
|
|
|
expect(await c()).toEqual(0);
|
|
expect(await d()).toEqual(0);
|
|
expect(await e()).toEqual(0);
|
|
|
|
a(1);
|
|
expect(await c()).toEqual(1);
|
|
expect(await d()).toEqual(2);
|
|
expect(await e()).toEqual(1);
|
|
|
|
a(2);
|
|
expect(await c()).toEqual(4);
|
|
expect(await d()).toEqual(6);
|
|
expect(await e()).toEqual(16);
|
|
|
|
a(3);
|
|
expect(await c()).toEqual(9);
|
|
expect(await d()).toEqual(12);
|
|
expect(await e()).toEqual(81);
|
|
done();
|
|
});
|
|
|
|
it('computes automatically when subscribed', async done => {
|
|
let runCount = 0;
|
|
let subRunCount = 0;
|
|
let currentValue = 1;
|
|
const a = prop(0);
|
|
const b = stream(
|
|
async val => {
|
|
runCount += 1;
|
|
expect(val).toEqual(currentValue);
|
|
return new Promise(resolve => setTimeout(() => resolve(val * val), 30));
|
|
},
|
|
[a]
|
|
);
|
|
|
|
// b does not evaluate
|
|
a(1);
|
|
expect(runCount).toEqual(0);
|
|
// b evaluates
|
|
expect(await b()).toEqual(1);
|
|
expect(runCount).toEqual(1);
|
|
// b does not evaluate
|
|
expect(await b()).toEqual(1);
|
|
expect(runCount).toEqual(1);
|
|
|
|
const cancelSubscription = b.subscribe(val => {
|
|
subRunCount += 1;
|
|
expect(val).toEqual(currentValue * currentValue);
|
|
});
|
|
|
|
currentValue = 3;
|
|
// b evaluates
|
|
a(3);
|
|
|
|
// b is triggered to update but hasn't yet
|
|
expect(runCount).toEqual(1);
|
|
expect(subRunCount).toEqual(0);
|
|
|
|
setTimeout(async () => {
|
|
// b should have updated now
|
|
expect(runCount).toEqual(2);
|
|
expect(subRunCount).toEqual(1);
|
|
|
|
// b does not evaluate
|
|
expect(await b()).toEqual(9);
|
|
expect(runCount).toEqual(2);
|
|
expect(subRunCount).toEqual(1);
|
|
done();
|
|
}, 40);
|
|
});
|
|
|
|
it('only computes once for overlapping calls', async done => {
|
|
let callCount = 0;
|
|
async function delayRun(a) {
|
|
return new Promise(resolve =>
|
|
setTimeout(() => {
|
|
callCount += 1;
|
|
resolve(callCount + a);
|
|
}, 10)
|
|
);
|
|
}
|
|
const a = prop(0);
|
|
const b = stream(delayRun, [a]);
|
|
|
|
expect(await b()).toEqual(1);
|
|
expect(callCount).toEqual(1);
|
|
|
|
a(1);
|
|
expect(await b()).toEqual(3);
|
|
expect(callCount).toEqual(2);
|
|
|
|
// Just calling sequentially should not re-evaluate
|
|
expect(await b()).toEqual(3);
|
|
expect(callCount).toEqual(2);
|
|
|
|
// Set b.dirty flag
|
|
a(2);
|
|
Promise.all([b(), b()])
|
|
.then(([res_1, res_2]) => {
|
|
expect(res_1).toEqual(5);
|
|
expect(res_2).toEqual(5);
|
|
expect(callCount).toEqual(3);
|
|
})
|
|
.finally(done);
|
|
});
|
|
});
|