Mirror: ๐ŸŽฉ A tiny but capable push & pull stream library for TypeScript and Flow

Add tests for WonkaJs timing functions. (#17)

This PR adds tests for some of the timing operators included in the `WonkaJs` module, specifically `delay`, `throttle`, `debounce`, and `sample`. Just chipping away at test coverage and having fun learning more `wonka` / observable-based programming ๐Ÿ˜….

It also adds `*.rei` files to our pre-commit hook and `yarn refmt` script, which I missed in #15 ๐Ÿ˜ž.

+320
__tests__/wonka_test.re
···
);
});
});
+
+
describe("web operators", () => {
+
describe("delay", () => {
+
open Expect;
+
open! Expect.Operators;
+
+
afterEach(() => Jest.useRealTimers());
+
+
it("should not emit values before specified delay", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.fromList([1, 2, 3]);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.delay(200, a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
expect(signals) == [||];
+
});
+
+
it("should emit values after specified delay", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.fromList([1, 2, 3]);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.delay(200, a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(400);
+
+
expect(signals) == [|Push(1), Push(2)|];
+
});
+
+
it("should emit an End signal when the source has emitted all values", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.fromList([1, 2, 3]);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.delay(200, a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(600);
+
+
expect(signals) == [|Push(1), Push(2), Push(3), End|];
+
});
+
});
+
+
describe("throttle", () => {
+
open Expect;
+
open! Expect.Operators;
+
+
afterEach(() => Jest.useRealTimers());
+
+
it(
+
"should not emit values before specified throttle (but include values on leading edge)",
+
() => {
+
Jest.useFakeTimers();
+
let a = Wonka.interval(100);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.throttle((. _) => 600, a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(400);
+
+
expect(signals) == [|Push(0)|];
+
},
+
);
+
+
it("should throttle emissions by the specified throttle", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.interval(100);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.throttle((. _) => 600, a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(1000);
+
+
expect(signals) == [|Push(0), Push(7)|];
+
});
+
+
it("should emit an End signal when the source has emitted all values", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.interval(100);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.throttle((. _) => 600, a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(1500);
+
+
expect(signals) == [|Push(0), Push(7), Push(14), End|];
+
});
+
});
+
+
describe("debounce", () => {
+
open Expect;
+
open! Expect.Operators;
+
+
afterEach(() => Jest.useRealTimers());
+
+
it(
+
"should not emit values if emitted before the debounce specified by the duration selector",
+
() => {
+
Jest.useFakeTimers();
+
let a = Wonka.fromList([1, 2, 3]);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.debounce((. _) => 1000, a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(500);
+
+
expect(signals) == [||];
+
},
+
);
+
+
it("should debounce emissions based on the duration selector", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.fromList([1, 2, 3]);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.debounce((. _) => 1000, a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(2000);
+
+
expect(signals) == [|Push(1), Push(2)|];
+
});
+
+
it("should emit an End signal when the source has emitted all values", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.fromList([1, 2, 3]);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.debounce((. _) => 1000, a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(3000);
+
+
expect(signals) == [|Push(1), Push(2), Push(3), End|];
+
});
+
});
+
+
describe("sample", () => {
+
open Expect;
+
open! Expect.Operators;
+
+
afterEach(() => Jest.useRealTimers());
+
+
it("should sample the last emitted value from a source", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.interval(50);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.sample(Wonka.interval(100), a);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(200);
+
+
expect(signals) == [|Push(1), Push(3)|];
+
});
+
+
it("should emit an End signal when the source has emitted all values", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.interval(50);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = WonkaJs.sample(Wonka.interval(100), a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(300);
+
+
expect(signals) == [|Push(1), Push(3), Push(5), End|];
+
});
+
});
+
});
+3 -3
package.json
···
"terser:cjs": "terser --config-file .terser.config.json -o ./dist/wonka.js ./dist/wonka.js",
"terser": "run-p terser:es terser:cjs",
"prettier": "prettier --write ./dist/*.js",
-
"refmt": "bsrefmt --in-place **/**/*.re",
+
"refmt": "bsrefmt --in-place **/**/*.{re,rei}",
"prebundle": "rimraf ./dist",
"bundle": "microbundle --external none --no-compress --no-sourcemap --format es,cjs",
"postbundle": "run-s terser prettier",
···
"terser": "^3.14.1"
},
"lint-staged": {
-
"*.re": [
+
"*.{re, rei}": [
"bsrefmt --in-place",
"git add"
]
···
"maxSize": "9 kB"
}
]
-
}
+
}
+1 -1
src/operators/wonka_operator_concatMap.rei
···
open Wonka_types;
-
let concatMap: ((.'a) => sourceT('b), sourceT('a), sinkT('b)) => unit;
+
let concatMap: ((. 'a) => sourceT('b), sourceT('a), sinkT('b)) => unit;
let concat: (array(sourceT('a)), sinkT('a)) => unit;
let concatAll: (sourceT(sourceT('a)), sinkT('a)) => unit;
+1 -1
src/operators/wonka_operator_filter.rei
···
open Wonka_types;
-
let filter: ((.'a) => bool, sourceT('a), sinkT('a)) => unit;
+
let filter: ((. 'a) => bool, sourceT('a), sinkT('a)) => unit;
+1 -1
src/operators/wonka_operator_map.rei
···
open Wonka_types;
-
let map: ((.'a) => 'b, sourceT('a), sinkT('b)) => unit;
+
let map: ((. 'a) => 'b, sourceT('a), sinkT('b)) => unit;
+1 -1
src/operators/wonka_operator_mergeMap.rei
···
open Wonka_types;
-
let mergeMap: ((.'a) => sourceT('b), sourceT('a), sinkT('b)) => unit;
+
let mergeMap: ((. 'a) => sourceT('b), sourceT('a), sinkT('b)) => unit;
let merge: (array(sourceT('a)), sinkT('a)) => unit;
let mergeAll: (sourceT(sourceT('a)), sinkT('a)) => unit;
let flatten: (sourceT(sourceT('a)), sinkT('a)) => unit;
+1 -1
src/operators/wonka_operator_onEnd.rei
···
open Wonka_types;
-
let onEnd: ((.unit) => unit, sourceT('a), sinkT('a)) => unit;
+
let onEnd: ((. unit) => unit, sourceT('a), sinkT('a)) => unit;
+2 -2
src/operators/wonka_operator_onPush.rei
···
open Wonka_types;
-
let onPush: ((.'a) => unit, sourceT('a), sinkT('a)) => unit;
-
let tap: ((.'a) => unit, sourceT('a), sinkT('a)) => unit;
+
let onPush: ((. 'a) => unit, sourceT('a), sinkT('a)) => unit;
+
let tap: ((. 'a) => unit, sourceT('a), sinkT('a)) => unit;
+1 -1
src/operators/wonka_operator_onStart.rei
···
open Wonka_types;
-
let onStart: ((.unit) => unit, sourceT('a), sinkT('a)) => unit;
+
let onStart: ((. unit) => unit, sourceT('a), sinkT('a)) => unit;
+1 -1
src/operators/wonka_operator_scan.rei
···
open Wonka_types;
-
let scan: ((.'b, 'a) => 'b, 'b, sourceT('a), sinkT('b)) => unit;
+
let scan: ((. 'b, 'a) => 'b, 'b, sourceT('a), sinkT('b)) => unit;
+1 -1
src/operators/wonka_operator_skipWhile.rei
···
open Wonka_types;
-
let skipWhile: ((.'a) => bool, sourceT('a), sinkT('a)) => unit;
+
let skipWhile: ((. 'a) => bool, sourceT('a), sinkT('a)) => unit;
+1 -1
src/operators/wonka_operator_switchMap.rei
···
open Wonka_types;
-
let switchMap: ((.'a) => sourceT('b), sourceT('a), sinkT('b)) => unit;
+
let switchMap: ((. 'a) => sourceT('b), sourceT('a), sinkT('b)) => unit;
let switchAll: (sourceT(sourceT('a)), sinkT('a)) => unit;
+1 -1
src/operators/wonka_operator_takeWhile.rei
···
open Wonka_types;
-
let takeWhile: ((.'a) => bool, sourceT('a), sinkT('a)) => unit;
+
let takeWhile: ((. 'a) => bool, sourceT('a), sinkT('a)) => unit;
+2 -2
src/sinks/wonka_sink_subscribe.rei
···
open Wonka_types;
-
let subscribe: ((.'a) => unit, sourceT('a)) => subscriptionT;
-
let forEach: ((.'a) => unit, sourceT('a)) => unit;
+
let subscribe: ((. 'a) => unit, sourceT('a)) => subscriptionT;
+
let forEach: ((. 'a) => unit, sourceT('a)) => unit;
+1 -1
src/sources/wonka_source_make.rei
···
open Wonka_types;
-
let make: ((.observerT('a)) => teardownT, sinkT('a)) => unit;
+
let make: ((. observerT('a)) => teardownT, sinkT('a)) => unit;
+2 -2
src/sources/wonka_source_primitives.rei
···
open Wonka_types;
-
let empty: (sinkT('a)) => unit;
-
let never: (sinkT('a)) => unit;
+
let empty: sinkT('a) => unit;
+
let never: sinkT('a) => unit;
+1 -1
src/web/wonka_operator_debounce.rei
···
open Wonka_types;
-
let debounce: ((.'a) => int, sourceT('a), sinkT('a)) => unit;
+
let debounce: ((. 'a) => int, sourceT('a), sinkT('a)) => unit;
+1 -1
src/web/wonka_operator_throttle.rei
···
open Wonka_types;
-
let throttle: ((.'a) => int, sourceT('a), sinkT('a)) => unit;
+
let throttle: ((. 'a) => int, sourceT('a), sinkT('a)) => unit;
+1 -5
src/web/wonka_source_fromListener.rei
···
open Wonka_types;
let fromListener:
-
(
-
('event => unit) => unit,
-
('event => unit) => unit,
-
sinkT('event)
-
) => unit;
+
(('event => unit) => unit, ('event => unit) => unit, sinkT('event)) => unit;