callbag-combine
Version:
Callbag factory that combines data from multiple callbag sources
358 lines (315 loc) • 8.3 kB
JavaScript
const test = require('tape');
const combine = require('./readme');
test('it combines 1 async finite listenable source', (t) => {
t.plan(14);
const downwardsExpectedType = [
[0, 'function'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
[2, 'undefined']
];
const downwardsExpected = [[1], [2], [3]];
function sourceA(type, data) {
if (type === 0) {
const sink = data;
let i = 0;
const id = setInterval(() => {
i++;
sink(1, i);
if (i === 3) {
clearInterval(id);
sink(2);
}
}, 100);
sink(0, sourceA);
}
}
function sink(type, data) {
const et = downwardsExpectedType.shift();
t.equals(type, et[0], 'downwards type is expected: ' + et[0]);
t.equals(typeof data, et[1], 'downwards data type is expected: ' + et[1]);
if (type === 1) {
const e = downwardsExpected.shift();
t.deepEquals(data, e, 'downwards data is expected: ' + JSON.stringify(e));
}
};
const source = combine(sourceA);
source(0, sink);
setTimeout(() => {
t.pass('nothing else happens');
t.end();
}, 700);
});
test('it combines 2 async finite listenable sources', (t) => {
t.plan(20);
const downwardsExpectedType = [
[0, 'function'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
[2, 'undefined']
];
const downwardsExpected = [
[2, 'a'],
[3, 'a'],
[4, 'a'],
[4, 'b'],
[5, 'b'],
];
function sourceA(type, data) {
if (type === 0) {
const sink = data;
let i = 0;
const id = setInterval(() => {
i++;
sink(1, i);
if (i === 5) {
clearInterval(id);
sink(2);
}
}, 100);
sink(0, sourceA);
}
}
function sourceB(type, data) {
if (type === 0) {
const sink = data;
setTimeout(() => { sink(1, 'a'); }, 230);
setTimeout(() => { sink(1, 'b'); }, 460);
setTimeout(() => { sink(2); }, 550);
sink(0, sourceB);
}
}
function sink(type, data) {
const et = downwardsExpectedType.shift();
t.equals(type, et[0], 'downwards type is expected: ' + et[0]);
t.equals(typeof data, et[1], 'downwards data type is expected: ' + et[1]);
if (type === 1) {
const e = downwardsExpected.shift();
t.deepEquals(data, e, 'downwards data is expected: ' + JSON.stringify(e));
}
};
const source = combine(sourceA, sourceB);
source(0, sink);
setTimeout(() => {
t.pass('nothing else happens');
t.end();
}, 700);
});
test('it returns a source that disposes upon upwards END', (t) => {
t.plan(16);
const upwardsExpected = [
[0, 'function'],
[2, 'undefined']
];
const downwardsExpectedType = [
[0, 'function'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
];
const downwardsExpected = [[10], [20], [30]];
function makeSource() {
let sent = 0;
let id;
const source = (type, data) => {
const e = upwardsExpected.shift();
t.equals(type, e[0], 'upwards type is expected: ' + e[0]);
t.equals(typeof data, e[1], 'upwards data is expected: ' + e[1]);
if (type === 0) {
const sink = data;
id = setInterval(() => {
sink(1, (++sent)*10);
}, 100);
sink(0, source);
} else if (type === 2) {
clearInterval(id);
}
};
return source;
};
function makeSink(type, data) {
let talkback;
return (type, data) => {
const et = downwardsExpectedType.shift();
t.equals(type, et[0], 'downwards type is expected: ' + et[0]);
t.equals(typeof data, et[1], 'downwards data type is expected: ' + et[1]);
if (type === 0) {
talkback = data;
}
if (type === 1) {
const e = downwardsExpected.shift();
t.deepEquals(data, e, 'downwards data is expected: ' + JSON.stringify(e));
}
if (downwardsExpected.length === 0) {
talkback(2);
}
};
}
const source = combine(makeSource());
const sink = makeSink();
source(0, sink);
setTimeout(() => {
t.pass('nothing else happens');
t.end();
}, 700);
});
test('it combines two infinite listenable sources', (t) => {
t.plan(26);
const upwardsExpectedA = [
[0, 'function'],
[2, 'undefined']
];
const upwardsExpectedB = [
[0, 'function'],
[2, 'undefined']
];
const downwardsExpectedType = [
[0, 'function'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
[1, 'object']
];
const downwardsExpected = [
[2, 'a'],
[3, 'a'],
[4, 'a'],
[4, 'b'],
[5, 'b']
];
function sourceA(start, sink) {
const e = upwardsExpectedA.shift();
t.equals(start, e[0], 'upwards A type is expected: ' + e[0]);
t.equals(typeof sink, e[1], 'upwards A data is expected: ' + e[1]);
if (start !== 0) return;
let i = 0;
const id = setInterval(() => {
sink(1, ++i);
}, 100);
sink(0, (type, data) => {
const e = upwardsExpectedA.shift();
t.equals(type, e[0], 'upwards A type is expected: ' + e[0]);
t.equals(typeof data, e[1], 'upwards A data is expected: ' + e[1]);
if (type === 2) {
clearInterval(id);
}
});
}
function sourceB(start, sink) {
const e = upwardsExpectedB.shift();
t.equals(start, e[0], 'upwards B type is expected: ' + e[0]);
t.equals(typeof sink, e[1], 'upwards B data is expected: ' + e[1]);
if (start !== 0) return;
let id;
id = setTimeout(() => {
sink(1, 'a');
id = setTimeout(() => {
sink(1, 'b');
id = setTimeout(() => {
sink(1, 'c');
id = setTimeout(() => {
sink(1, 'd');
}, 230);
}, 230);
}, 230);
}, 230);
sink(0, (type, data) => {
const e = upwardsExpectedB.shift();
t.equals(type, e[0], 'upwards B type is expected: ' + e[0]);
t.equals(typeof data, e[1], 'upwards B data is expected: ' + e[1]);
if (type === 2) {
clearTimeout(id);
}
});
}
function makeSink() {
let talkback;
return (type, data) => {
const et = downwardsExpectedType.shift();
t.equals(type, et[0], 'downwards type is expected: ' + et[0]);
t.equals(typeof data, et[1], 'downwards data type is expected: ' + et[1]);
if (type === 0) {
talkback = data;
}
if (type === 1) {
const e = downwardsExpected.shift();
t.deepEquals(data, e, 'downwards data is expected: ' + JSON.stringify(e));
}
if (downwardsExpected.length === 0) {
talkback(2);
}
};
}
const source = combine(sourceA, sourceB);
const sink = makeSink();
source(0, sink);
setTimeout(() => {
t.pass('nothing else happens');
t.end();
}, 800);
});
test('it combines pullable sources', (t) => {
t.plan(19);
const downwardsExpectedType = [
[0, 'function'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
[1, 'object'],
[2, 'undefined']
];
const downwardsExpected = [
[1, 'a'],
[2, 'a'],
[3, 'a'],
[3, 'b'],
[3, 'c']
];
function makePullable(values) {
return (start, sink) => {
let completed = false;
sink(0, (type) => {
if (completed) return;
if (type === 1) {
const value = values.shift();
if (values.length === 0) {
completed = true;
};
sink(1, value);
if (completed) {
sink(2);
}
}
});
};
}
function makeSink() {
let talkback;
return (type, data) => {
const et = downwardsExpectedType.shift();
t.equals(type, et[0], 'downwards type is expected: ' + et[0]);
t.equals(typeof data, et[1], 'downwards data type is expected: ' + et[1]);
if (type === 2) return;
if (type === 0) {
talkback = data;
}
if (type === 1) {
const e = downwardsExpected.shift();
t.deepEquals(data, e, 'downwards data is expected: ' + JSON.stringify(e));
}
talkback(1);
};
}
const source = combine(
makePullable([1, 2, 3]),
makePullable(['a', 'b', 'c'])
);
source(0, makeSink());
t.end();
});