···
-
import { Source, Sink, Operator, Signal, SignalKind, TalkbackKind, TalkbackFn } from '../types';
import { push, start } from '../helpers';
import * as sources from '../sources';
import * as sinks from '../sinks';
import * as operators from '../operators';
-
/* This tests a noop operator for passive Pull talkback signals.
-
A Pull will be sent from the sink upwards and should pass through
-
the operator until the source receives it, which then pushes a
-
const passesPassivePull = (operator: Operator<any, any>, output: any = 0) => {
-
it('responds to Pull talkback signals (spec)', () => {
-
let talkback: TalkbackFn | null = null;
-
const values: any[] = [];
-
const source: Source<any> = sink => {
-
if (!pushes && signal === TalkbackKind.Pull) {
-
const sink: Sink<any> = signal => {
-
expect(signal).not.toBe(SignalKind.End);
-
if (signal === SignalKind.End) {
-
} else if (signal.tag === SignalKind.Push) {
-
values.push(signal[0]);
-
operator(source)(sink);
-
// The Start signal should always come in immediately
-
expect(talkback).not.toBe(null);
-
// No Push signals should be issued initially
-
expect(values).toEqual([]);
-
// When pulling a value we expect an immediate response
-
talkback!(TalkbackKind.Pull);
-
expect(values).toEqual([output]);
-
/* This tests a noop operator for regular, active Push signals.
-
A Push will be sent downwards from the source, through the
-
operator to the sink. Pull events should be let through from
-
the sink after every Push event. */
-
const passesActivePush = (operator: Operator<any, any>, result: any = 0) => {
-
it('responds to eager Push signals (spec)', () => {
-
const values: any[] = [];
-
let talkback: TalkbackFn | null = null;
-
let sink: Sink<any> | null = null;
-
const source: Source<any> = _sink => {
-
if (signal === TalkbackKind.Pull) pulls++;
-
operator(source)(signal => {
-
expect(signal).not.toBe(SignalKind.End);
-
if (signal === SignalKind.End) {
-
} else if (signal.tag === SignalKind.Start) {
-
} else if (signal.tag === SignalKind.Push) {
-
values.push(signal[0]);
-
talkback!(TalkbackKind.Pull);
-
// No Pull signals should be issued initially
-
// When pushing a value we expect an immediate response
-
expect(values).toEqual([result]);
-
// Subsequently the Pull signal should have travelled upwards
-
/* This tests a noop operator for Close talkback signals from the sink.
-
A Close signal will be sent, which should be forwarded to the source,
-
which then ends the communication without sending an End signal. */
-
const passesSinkClose = (operator: Operator<any, any>) => {
-
it('responds to Close signals from sink (spec)', () => {
-
let talkback: TalkbackFn | null = null;
-
const source: Source<any> = sink => {
-
if (signal === TalkbackKind.Pull && !closing) {
-
} else if (signal === TalkbackKind.Close) {
-
const sink: Sink<any> = signal => {
-
expect(signal).not.toBe(SignalKind.End);
-
if (signal === SignalKind.End) {
-
} else if (signal.tag === SignalKind.Push) {
-
talkback!(TalkbackKind.Close);
-
operator(source)(sink);
-
// When pushing a value we expect an immediate close signal
-
talkback!(TalkbackKind.Pull);
-
expect(closing).toBe(1);
-
/* This tests a noop operator for End signals from the source.
-
A Push and End signal will be sent after the first Pull talkback
-
signal from the sink, which shouldn't lead to any extra Close or Pull
-
const passesSourceEnd = (operator: Operator<any, any>, result: any = 0) => {
-
it('passes on immediate Push then End signals from source (spec)', () => {
-
const signals: Signal<any>[] = [];
-
let talkback: TalkbackFn | null = null;
-
const source: Source<any> = sink => {
-
expect(signal).not.toBe(TalkbackKind.Close);
-
if (signal === TalkbackKind.Pull) {
-
const sink: Sink<any> = signal => {
-
if (signal === SignalKind.End) {
-
} else if (signal.tag === SignalKind.Push) {
-
operator(source)(sink);
-
// When pushing a value we expect an immediate Push then End signal
-
talkback!(TalkbackKind.Pull);
-
expect(ending).toBe(1);
-
expect(signals).toEqual([push(result), SignalKind.End]);
-
// Also no additional pull event should be created by the operator
-
/* This tests a noop operator for End signals from the source
-
after the first pull in response to another.
-
This is similar to passesSourceEnd but more well behaved since
-
mergeMap/switchMap/concatMap are eager operators. */
-
const passesSourcePushThenEnd = (operator: Operator<any, any>, result: any = 0) => {
-
it('passes on End signals from source (spec)', () => {
-
const signals: Signal<any>[] = [];
-
let talkback: TalkbackFn | null = null;
-
const source: Source<any> = sink => {
-
expect(signal).not.toBe(TalkbackKind.Close);
-
if (signal === TalkbackKind.Pull) {
-
const sink: Sink<any> = signal => {
-
if (signal === SignalKind.End) {
-
} else if (signal.tag === SignalKind.Push) {
-
talkback!(TalkbackKind.Pull);
-
operator(source)(sink);
-
// When pushing a value we expect an immediate Push then End signal
-
talkback!(TalkbackKind.Pull);
-
expect(ending).toBe(1);
-
expect(signals).toEqual([push(result), push(result), SignalKind.End]);
-
/* This tests a noop operator for Start signals from the source.
-
When the operator's sink is started by the source it'll receive
-
a Start event. As a response it should never send more than one
-
Start signals to the sink. */
-
const passesSingleStart = (operator: Operator<any, any>) => {
-
it('sends a single Start event to the incoming sink (spec)', () => {
-
const source: Source<any> = sink => {
-
const sink: Sink<any> = signal => {
-
if (signal !== SignalKind.End && signal.tag === SignalKind.Start) {
-
// When starting the operator we expect a single start event on the sink
-
operator(source)(sink);
-
expect(starts).toBe(1);
-
/* This tests a noop operator for silence after End signals from the source.
-
When the operator receives the End signal it shouldn't forward any other
-
signals to the sink anymore.
-
This isn't a strict requirement, but some operators should ensure that
-
all sources are well behaved. This is particularly true for operators
-
that either Close sources themselves or may operate on multiple sources. */
-
const passesStrictEnd = (operator: Operator<any, any>) => {
-
it('stops all signals after End has been received (spec: strict end)', () => {
-
const signals: Signal<any>[] = [];
-
const source: Source<any> = sink => {
-
if (signal === TalkbackKind.Pull) {
-
const sink: Sink<any> = signal => {
-
if (signal === SignalKind.End) {
-
} else if (signal.tag === SignalKind.Push) {
-
signal[0](TalkbackKind.Pull);
-
operator(source)(sink);
-
// The Push signal should've been dropped
-
expect(signals).toEqual([SignalKind.End]);
-
it('stops all signals after Close has been received (spec: strict close)', () => {
-
const signals: Signal<any>[] = [];
-
const source: Source<any> = sink => {
-
if (signal === TalkbackKind.Close) {
-
const sink: Sink<any> = signal => {
-
if (signal === SignalKind.End) {
-
} else if (signal.tag === SignalKind.Push) {
-
signal[0](TalkbackKind.Close);
-
operator(source)(sink);
-
// The Push signal should've been dropped
-
expect(signals).toEqual([]);
-
/* This tests an immediately closing operator for End signals to
-
the sink and Close signals to the source.
-
When an operator closes immediately we expect to see a Close
-
signal at the source and an End signal to the sink, since the
-
closing operator is expected to end the entire chain. */
-
const passesCloseAndEnd = (closingOperator: Operator<any, any>) => {
-
it('closes the source and ends the sink correctly (spec: ending operator)', () => {
-
const source: Source<any> = sink => {
-
// For some operator tests we do need to send a single value
-
if (signal === TalkbackKind.Pull) {
-
const sink: Sink<any> = signal => {
-
if (signal === SignalKind.End) {
-
} else if (signal.tag === SignalKind.Start) {
-
signal[0](TalkbackKind.Pull);
-
// We expect the operator to immediately end and close
-
closingOperator(source)(sink);
-
expect(closing).toBe(1);
-
expect(ending).toBe(1);
-
const passesAsyncSequence = (operator: Operator<any, any>, result: any = 0) => {
-
it('passes an async push with an async end (spec)', () => {
-
const signals: Signal<any>[] = [];
-
const source: Source<any> = sink => {
-
if (signal === TalkbackKind.Pull && !hasPushed) {
-
setTimeout(() => sink(push(0)), 10);
-
setTimeout(() => sink(SignalKind.End), 20);
-
const sink: Sink<any> = signal => {
-
if (signal === SignalKind.End) {
-
} else if (signal.tag === SignalKind.Push) {
-
signal[0](TalkbackKind.Pull);
-
// We initially expect to see the push signal
-
// Afterwards after all timers all other signals come in
-
operator(source)(sink);
-
expect(signals.length).toBe(0);
-
jest.advanceTimersByTime(5);
-
expect(hasPushed).toBeTruthy();
-
expect(signals).toEqual([push(result), SignalKind.End]);