xstream
Version:
An extremely intuitive, small, and fast functional reactive stream library for JavaScript
173 lines (155 loc) • 5.54 kB
text/typescript
/// <reference path="../../typings/globals/mocha/index.d.ts" />
/// <reference path="../../typings/globals/node/index.d.ts" />
import xs, {Stream, Listener} from '../../src/index';
import flattenConcurrently from '../../src/extra/flattenConcurrently';
import * as assert from 'assert';
describe('flattenConcurrently (extra)', () => {
describe('with map', () => {
it('should expand each periodic event with 3 sync events', (done) => {
const stream = xs.periodic(100).take(3)
.map(i => xs.of(1 + i, 2 + i, 3 + i))
.compose(flattenConcurrently);
const expected = [1, 2, 3, 2, 3, 4, 3, 4, 5];
stream.addListener({
next: (x: number) => {
assert.equal(x, expected.shift());
},
error: (err: any) => done(err),
complete: () => {
assert.equal(expected.length, 0);
done();
},
});
});
it('should return a flat stream with correct TypeScript types', (done) => {
const streamStrings: Stream<string> = Stream.create({
start: (listener: Listener<string>) => {},
stop: () => {}
});
const streamBooleans: Stream<boolean> = Stream.create({
start: (listener: Listener<boolean>) => {},
stop: () => {}
});
// Type checked by the compiler. Without Stream<boolean> it does not compile.
const flat: Stream<boolean> = streamStrings.map(x => streamBooleans)
.compose(flattenConcurrently);
done();
});
it('should expand 3 sync events as a periodic each', (done) => {
const stream = xs.of(0, 1, 2)
.map(i => xs.periodic(100 * i).take(2).map(x => `${i}${x}`))
.compose(flattenConcurrently);
// ---x---x---x---x---x---x
// ---00--01
// -------10------11
// -----------20----------21
const expected = ['00', '01', '10', '20', '11', '21'];
stream.addListener({
next: (x: string) => {
assert.equal(x, expected.shift());
},
error: (err: any) => done(err),
complete: () => {
assert.equal(expected.length, 0);
done();
},
});
});
it('should expand 3 async events as a periodic each', (done) => {
const stream = xs.periodic(140).take(3)
.map(i =>
xs.periodic(100 * (i < 2 ? 1 : i)).take(3).map(x => `${i}${x}`)
)
.compose(flattenConcurrently);
// ---x---x---x---x---x---x---x---x---x---x---x---x
// ---00--01--02
// ----10--11--12
// ------------20-----------21----------22
const expected = ['00', '01', '10', '02', '11', '12', '20', '21', '22'];
stream.addListener({
next: (x: string) => {
assert.equal(x, expected.shift());
},
error: (err: any) => done(err),
complete: () => {
assert.equal(expected.length, 0);
done();
},
});
});
it('should expand 3 async events as a periodic each, no optimization', (done) => {
const stream = xs.periodic(140).take(3)
.map(i =>
xs.periodic(100 * (i < 2 ? 1 : i)).take(3).map(x => `${i}${x}`)
)
.filter(() => true) // breaks the optimization map+flattenConcurrently
.compose(flattenConcurrently);
// ---x---x---x---x---x---x---x---x---x---x---x---x
// ---00--01--02
// ----10--11--12
// ------------20-----------21----------22
const expected = ['00', '01', '10', '02', '11', '12', '20', '21', '22'];
stream.addListener({
next: (x: string) => {
assert.equal(x, expected.shift());
},
error: (err: any) => done(err),
complete: () => {
assert.equal(expected.length, 0);
done();
}
});
});
it('should propagate user mistakes in project as errors', (done) => {
const source = xs.periodic(30).take(1);
const stream = source.map(
x => {
const y = (<string> <any> x).toLowerCase();
return xs.of(y);
}
).compose(flattenConcurrently);
stream.addListener({
next: () => done('next should not be called'),
error: (err) => {
assert.notStrictEqual(err.message.match(/is not a function$/), null);
done();
},
complete: () => {
done('complete should not be called');
},
});
});
});
describe('with filter+map fusion', () => {
it('should execute the predicate, the projection, and the flattening', (done) => {
let predicateCallCount = 0;
let projectCallCount = 0;
const stream = xs.periodic(140).take(3)
.filter(i => {
predicateCallCount += 1;
return i % 2 === 0;
})
.map(i => {
projectCallCount += 1;
return xs.periodic(100 * (i < 2 ? 1 : i)).take(3).map(x => `${i}${x}`);
})
.compose(flattenConcurrently);
// ---x---x---x---x---x---x---x---x---x---x---x---x
// ---00--01--02
// ------------20-----------21----------22
const expected = ['00', '01', '02', '20', '21', '22'];
stream.addListener({
next: (x: string) => {
assert.equal(x, expected.shift());
},
error: (err: any) => done(err),
complete: () => {
assert.equal(expected.length, 0);
assert.equal(predicateCallCount, 3);
assert.equal(projectCallCount, 2);
done();
}
});
});
});
});