Introduction
terminal 提供了制作终端用户界面的模型。 用户看到一个显示,它是一个atom 或一个显示列表。 一个原子是一个可能带有文本标记的边框框。 使用 streamer,显示器可以对用户或环境生成的事件做出反应。
terminal 受益于 blessed 引入的树状终端图形的高级建模。 它还借鉴了 Lisp 编程语言家族的工具和术语。
How To Use Terminal
将终端添加到项目中:
$ npm install @acransac/terminal
并导入所需的功能:
const { atom, column, compose, cons, emptyList, indent, inline, label, renderer, row, show, sizeHeight, sizeWidth, TerminalTest, vindent } = require('@acransac/terminal');
Make An Inert Atomic Display
渲染引擎使用renderer
进行初始化。 后者返回函数以呈现显示并终止引擎。 原子显示是使用 atom
创建的,并传递给渲染函数。 然后,引擎终止。 一个原子的盒子可以用 label
来标记:
renderer:: Maybe -> (显示->(),()->())
Parameter / Returned |
Type |
Description |
output |
Maybe\ |
A writable Node.js stream to which the displayed characters and escape sequences are written. Default: process.stdout |
returned |
(Display -> (), () -> ()) |
An array of two functions. The first renders on the output the display passed as argument. The second tears down the rendering system |
atom:: 字符串 -> 显示
Parameter |
Type |
Description |
content |
String |
The text to print within the atom box |
label:: (Atom, String) -> Atom
Parameter |
Type |
Description |
atom |
Atom\ |
The atom to label |
title |
String |
The label's text |
|
|
|
Examples: |
|
|
1.
const { atom, renderer } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(atom("abc"));
setTimeout(terminate, 2000);
$ node example.js
2.
const { atom, label, renderer } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(label(atom("abc"), "example"));
setTimeout(terminate, 2000);
$ node example.js
Make An Inert List Display
如前所述,终端 的工具继承了 Lisp 方言的基本概念。 显示是一种递归结构,表示为显示的嵌套列表。 它是通过cons
'ing 显示到列表中构建的,以emptyList
结束这种分支链。 后者可以使用 row
或 column
来确定高度或宽度的尺寸:
缺点:: (Display, List) -> 列表<显示>
Parameter |
Type |
Description |
display |
Display |
The display, atom or list, to prepend |
list |
List\ |
The list of displays to prepend to |
<代码>emptyList:: () -> 列表<显示>
Returned |
Type |
Description |
returned |
List\ |
A list display printing nothing. The starting point to which useful displays are prepended |
<代码>行::数字-> 列表<显示>
Parameter |
Type |
Description |
height |
Number |
The empty list's height proportionally to the underlying list's height if any. Otherwise, it is proportional to the screen's height. Expressed in percentage |
<代码>列::编号-> List
Parameter |
Type |
Description |
width |
Number |
The empty list's width proportionally to the underlying list's width if any. Otherwise, it is proportional to the screen's width. Expressed in percentage |
|
|
|
Examples: |
|
|
1.
const { atom, cons, emptyList, renderer } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(cons(atom("abc"), emptyList()));
setTimeout(terminate, 2000);
$ node example.js
注意:最后一个示例显示看起来与第一个相同,只是它是一个列表而不是一个原子。
2.
const { atom, cons, renderer, row } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(cons(atom("abc"), row(50)));
setTimeout(terminate, 2000);
$ node example.js
3.
const { atom, column, cons, renderer } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(cons(atom("abc"), column(50)));
setTimeout(terminate, 2000);
$ node example.js
Size And Position Displays
sizeWidth
和 sizeHeight
用于调整原子的大小。 indent
和 vindent
位置显示。 它们允许显示具有多个原子或列表可见的列表。 此外,inline
将原子列表放在一行中:
<代码>sizeWidth:: (Number, Atom) -> 原子<显示>
Parameter |
Type |
Description |
size |
Number |
The atom's width proportionally to the underlying list's width if any. Otherwise, it is proportional to the screen's width. Expressed in percentage |
atom |
Atom\ |
The atom to resize |
<代码>sizeHeight:: (Number, Atom) -> 原子<显示>
Parameter |
Type |
Description |
size |
Number |
The atom's height proportionally to the underlying list's height if any. Otherwise, it is proportional to the screen's height. Expressed in percentage |
atom |
Atom\ |
The atom to resize |
缩进:: (数字, 显示) -> 显示
Parameter |
Type |
Description |
offset |
Number |
The display's offset from the underlying list's left edge if any. Otherwise, it is from the screen's left edge. Expressed in percentage of the underlier's width |
display |
Display |
The display to move |
vindent:: (Number, Display) -> 显示
Parameter |
Type |
Description |
offset |
Number |
The display's offset from the underlying list's top edge if any. Otherwise, it is from the screen's top edge. Expressed in percentage of the underlier's height |
display |
Display |
The display to move |
inline::List>; -> 显示
Parameter |
Type |
Description |
lat |
List\> |
A display that is a list of atoms with specified widths that are to be indented so that they show in a row |
|
|
|
Examples: |
|
|
1.
const { atom, indent, renderer, sizeHeight, sizeWidth, vindent } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(vindent(25, indent(25, sizeHeight(50, sizeWidth(50, atom("abc"))))));
setTimeout(terminate, 2000);
$ node example.js
2.
const { atom, cons, emptyList, indent, renderer, vindent } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(vindent(10, indent(10, cons(atom("abc"), emptyList()))));
setTimeout(terminate, 2000);
$ node example.js
3.
const { atom, cons, emptyList, indent, renderer, sizeWidth } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(cons(sizeWidth(50, atom("abc")),
cons(indent(50, sizeWidth(50, atom("def"))),
emptyList())));
setTimeout(terminate, 2000);
$ node example.js
4.
const { atom, cons, emptyList, inline, renderer, sizeWidth } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(inline(cons(sizeWidth(50, atom("abc")),
cons(sizeWidth(50, atom("def")),
emptyList()))));
setTimeout(terminate, 2000);
$ node example.js
Make A Reactive Display
例如,当用户与应用程序交互或接收到网络消息时,反应式显示会发生变化。 terminal 使用 streamer 框架来监听事件,并且将响应式显示集成到附加到来源。 此集成由 compose
承担,它将流分发到一系列 组件 并将它们的输出注入到 模板 中。 compose
立即与通过将渲染函数传递给 show
生成的函数链接起来,并执行注入模板的渲染:
<代码>组件::任何... -> 任何-> 流 -> 作曲家句柄
Parameter / Returned |
Type |
Description |
parameters |
Any… |
The state of the component |
predecessor |
Any |
The output of the component on the last event processed |
returned |
Stream -> ComposerHandle |
The logic of the component which processes the stream and returns to compose a handle to the parameters and output of the form f => f(updatedParameters)(output) . Using this handle, compose provides the parameters and predecessor to the component on the next event, and injects the ouput into the template |
<代码>模板::组件... -> 显示
Parameter |
Type |
Description |
components |
Component… |
A series of components whose output values are passed as arguments to the template |
compose:: (Template, Component...) -> 作曲家
Parameter |
Type |
Description |
template |
Template |
The template organizing the display |
reactiveComponents |
Component… |
A sequence of components parameterizing the template |
show::显示-> () -> 作曲家-> Process
Parameter |
Type |
Description |
render |
Display -> () |
The render function returned by renderer |
|
|
|
Example: |
|
|
const { continuation, forget, later, now, Source, StreamerTest, value } = require('@acransac/streamer');
const { atom, compose, renderer, show } = require('@acransac/terminal');
const [render, terminate] = renderer();
const loop = async (stream) => {
if (value(now(stream)) === "end") {
return terminate();
}
else {
return loop(await continuation(now(stream))(forget(await later(stream))));
}
};
const template = component => atom(component);
const component = noParameters => predecessor => stream => {
const processed = predecessor ? predecessor : "";
if (value(now(stream)) === "end") {
return f => f(noParameters)(processed);
}
else {
return f => f(noParameters)(`${processed}${value(now(stream))}`);
}
};
Source.from(StreamerTest.emitSequence(["a", "b", "c", "end"], 1000), "onevent")
.withDownstream(async (stream) => loop(await show(render)(compose(template, component))(stream)));
$ node example.js
Test The Display
TerminalTest.reviewDisplays
引入了对一系列显示的测试。 使用 makeTestableInertDisplay
或 makeTestableReactiveDisplay
创建显示测试。 运行时,执行此检查的脚本接受一个命令行选项:look
依次显示显示,以便可以直观地检查它们。 save
将显示的字符和转义序列写入文件,以便可以通过编程方式验证它们。 control
运行实际测试,将生成的显示与控制文件进行比较并记录成功和失败。 这是默认选项:
TerminalTest.reviewDisplays:: ([TestableDisplay], Maybe) -> ()
Parameter |
Type |
Description |
testableDisplays |
[TestableDisplay] |
An array of testable displays, whether inert or reactive |
testSuiteName |
Maybe\ |
The name of the test suite that appears in logs. Default: Test Suite |
TerminalTest.makeTestableInertDisplay:: (() -> Display, String) -> 可测试显示
Parameter |
Type |
Description |
display |
() -> Display |
A deferred inert display |
testName |
String |
The name of the test that appears in logs |
TerminalTest.makeTestableReactiveDisplay:: (ReactiveDisplayTest, String, Maybe) -> 可测试显示
Parameter |
Type |
Description |
produceDisplay |
ReactiveDisplayTest |
The test function displaying what is to be checked. Its signature depends on the setup done by the initializer. By default, it should provide a placeholder for the render function as first argument, and then a placeholder for the continuation of the test suite. The test runner provides the actual functions when executing |
testName |
String |
The name of the test that appears in logs |
init |
Maybe\ |
Logic to execute before running the test. Default: calls renderer |
<代码>TestInitializer:: (Stream.Writable, ReactiveDisplayTest, () -> ()) -> ()
Parameter |
Type |
Description |
displayTarget |
Stream.Writable |
A reference to the target for writing the characters and escape sequences of the display. It could be a file or the standard output depending on the mode of the test runner |
test |
ReactiveDisplayTest |
A reference to the test function to call once the initialization is done |
finish |
() -> () |
A reference to the continuation of the test to call once the test is done |
|
|
|
Examples: |
|
|
1.
const { continuation, forget, later, now, Source, StreamerTest, value } = require('@acransac/streamer');
const { atom, compose, show, TerminalTest } = require('@acransac/terminal');
function reactiveDisplayTest(render, finish) {
const loop = async (stream) => {
if (value(now(stream)) === "end") {
return finish();
}
else {
return loop(await continuation(now(stream))(forget(await later(stream))));
}
};
const template = component => atom(component);
const component = noParameters => predecessor => stream => {
const processed = predecessor ? predecessor : "";
if (value(now(stream)) === "end") {
return f => f(noParameters)(processed);
}
else {
return f => f(noParameters)(`${processed}${value(now(stream))}`);
}
};
Source.from(StreamerTest.emitSequence(["a", "b", "c", "end"], 1000), "onevent")
.withDownstream(async (stream) => loop(await show(render)(compose(template, component))(stream)));
}
TerminalTest.reviewDisplays([
TerminalTest.makeTestableInertDisplay(() => atom("abc"), "Inert Display"),
TerminalTest.makeTestableReactiveDisplay(reactiveDisplayTest, "Reactive Display")
], "Example Tests");
$ node example.js look
$ node example.js save
$ node example.js control
--------------------
Example Tests:
2 / 2 test(s) passed
--------------------
2.
const { continuation, forget, later, now, Source, StreamerTest, value } = require('@acransac/streamer');
const { atom, compose, renderer, show, TerminalTest } = require('@acransac/terminal');
function reactiveDisplayTest(render, finish) {
const loop = async (stream) => {
if (value(now(stream)) === "end") {
return finish();
}
else {
return loop(await continuation(now(stream))(forget(await later(stream))));
}
};
const template = component => atom(component);
const component = noParameters => predecessor => stream => {
const processed = predecessor ? predecessor : "";
if (value(now(stream)) === "end") {
return f => f(noParameters)(processed);
}
else {
return f => f(noParameters)(`${processed}${value(now(stream))}`);
}
};
return async (stream) => loop(await show(render)(compose(template, component))(stream));
}
function testInitializer(displayTarget, test, finish) {
const [render, terminate] = renderer(displayTarget);
const conclude = () => {
terminate();
return finish();
}
Source.from(StreamerTest.emitSequence(["a", "b", "c", "end"], 1000), "onevent")
.withDownstream(async (stream) => test(render, conclude)(stream));
}
TerminalTest.reviewDisplays([
TerminalTest.makeTestableReactiveDisplay(reactiveDisplayTest,
"Reactive Display With Initializer",
testInitializer)
], "Example Tests");
$ node example.js look
$ node example.js save
$ node example.js control
--------------------
Example Tests:
1 / 1 test(s) passed
--------------------
Introduction
terminal provides a model to make terminal user interfaces. The user sees a display which is either an atom or a list of displays. An atom is a possibly labelled bordered box with text. Using streamer, a display can react to events generated by the user or the environment.
terminal benefits from the high-level modelling of terminal graphics as a tree introduced by blessed. It also borrows its tools and terminology from the Lisp family of programming languages.
How To Use Terminal
Add terminal to a project with:
$ npm install @acransac/terminal
and import the needed functionalities:
const { atom, column, compose, cons, emptyList, indent, inline, label, renderer, row, show, sizeHeight, sizeWidth, TerminalTest, vindent } = require('@acransac/terminal');
Make An Inert Atomic Display
The rendering engine is initialized with renderer
. The latter returns functions to render a display and terminate the engine. An atomic display is created with atom
and passed to the render function. Then, the engine is terminated. An atom's box can be labelled with label
:
renderer:: Maybe<Stream.Writable> -> (Display -> (), () -> ())
Parameter / Returned |
Type |
Description |
output |
Maybe\ |
A writable Node.js stream to which the displayed characters and escape sequences are written. Default: process.stdout |
returned |
(Display -> (), () -> ()) |
An array of two functions. The first renders on the output the display passed as argument. The second tears down the rendering system |
atom:: String -> Display
Parameter |
Type |
Description |
content |
String |
The text to print within the atom box |
label:: (Atom<Display>, String) -> Atom<Display>
Parameter |
Type |
Description |
atom |
Atom\ |
The atom to label |
title |
String |
The label's text |
|
|
|
Examples: |
|
|
1.
const { atom, renderer } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(atom("abc"));
setTimeout(terminate, 2000);
$ node example.js
2.
const { atom, label, renderer } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(label(atom("abc"), "example"));
setTimeout(terminate, 2000);
$ node example.js
Make An Inert List Display
As mentioned before, terminal's tooling inherits the fundamental notions of the Lisp dialects. A display is a recursive structure expressed as nested lists of displays. It is built by cons
'ing displays onto lists, ending such ramified chains with the emptyList
. The latter can be dimensioned in height or in width with row
or column
:
cons:: (Display, List<Display>) -> List<Display>
Parameter |
Type |
Description |
display |
Display |
The display, atom or list, to prepend |
list |
List\ |
The list of displays to prepend to |
emptyList:: () -> List<Display>
Returned |
Type |
Description |
returned |
List\ |
A list display printing nothing. The starting point to which useful displays are prepended |
row:: Number -> List<Display>
Parameter |
Type |
Description |
height |
Number |
The empty list's height proportionally to the underlying list's height if any. Otherwise, it is proportional to the screen's height. Expressed in percentage |
column:: Number -> List<Display>
Parameter |
Type |
Description |
width |
Number |
The empty list's width proportionally to the underlying list's width if any. Otherwise, it is proportional to the screen's width. Expressed in percentage |
|
|
|
Examples: |
|
|
1.
const { atom, cons, emptyList, renderer } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(cons(atom("abc"), emptyList()));
setTimeout(terminate, 2000);
$ node example.js
Note: The last example display looks the same as the first one, except that it is a list and not an atom.
2.
const { atom, cons, renderer, row } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(cons(atom("abc"), row(50)));
setTimeout(terminate, 2000);
$ node example.js
3.
const { atom, column, cons, renderer } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(cons(atom("abc"), column(50)));
setTimeout(terminate, 2000);
$ node example.js
Size And Position Displays
sizeWidth
and sizeHeight
are used to size atoms. indent
and vindent
position displays. They allow to make list displays with several atoms or lists visible. Also, inline
places a list of atoms in a row:
sizeWidth:: (Number, Atom<Display>) -> Atom<Display>
Parameter |
Type |
Description |
size |
Number |
The atom's width proportionally to the underlying list's width if any. Otherwise, it is proportional to the screen's width. Expressed in percentage |
atom |
Atom\ |
The atom to resize |
sizeHeight:: (Number, Atom<Display>) -> Atom<Display>
Parameter |
Type |
Description |
size |
Number |
The atom's height proportionally to the underlying list's height if any. Otherwise, it is proportional to the screen's height. Expressed in percentage |
atom |
Atom\ |
The atom to resize |
indent:: (Number, Display) -> Display
Parameter |
Type |
Description |
offset |
Number |
The display's offset from the underlying list's left edge if any. Otherwise, it is from the screen's left edge. Expressed in percentage of the underlier's width |
display |
Display |
The display to move |
vindent:: (Number, Display) -> Display
Parameter |
Type |
Description |
offset |
Number |
The display's offset from the underlying list's top edge if any. Otherwise, it is from the screen's top edge. Expressed in percentage of the underlier's height |
display |
Display |
The display to move |
inline:: List<Atom<Display>> -> Display
Parameter |
Type |
Description |
lat |
List\> |
A display that is a list of atoms with specified widths that are to be indented so that they show in a row |
|
|
|
Examples: |
|
|
1.
const { atom, indent, renderer, sizeHeight, sizeWidth, vindent } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(vindent(25, indent(25, sizeHeight(50, sizeWidth(50, atom("abc"))))));
setTimeout(terminate, 2000);
$ node example.js
2.
const { atom, cons, emptyList, indent, renderer, vindent } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(vindent(10, indent(10, cons(atom("abc"), emptyList()))));
setTimeout(terminate, 2000);
$ node example.js
3.
const { atom, cons, emptyList, indent, renderer, sizeWidth } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(cons(sizeWidth(50, atom("abc")),
cons(indent(50, sizeWidth(50, atom("def"))),
emptyList())));
setTimeout(terminate, 2000);
$ node example.js
4.
const { atom, cons, emptyList, inline, renderer, sizeWidth } = require('@acransac/terminal');
const [render, terminate] = renderer();
render(inline(cons(sizeWidth(50, atom("abc")),
cons(sizeWidth(50, atom("def")),
emptyList()))));
setTimeout(terminate, 2000);
$ node example.js
Make A Reactive Display
A reactive display changes as the user interacts with the application or network messages are received, for example. terminal uses the streamer framework to listen to events and a reactive display is integrated into the process attached to the source. This integration is borne by compose
which distributes the stream to a series of components and injects their output into a template. compose
is immediately chained with the function generated by passing the render function to show
and that executes the rendering of the injected template:
Component:: Any... -> Any -> Stream -> ComposerHandle
Parameter / Returned |
Type |
Description |
parameters |
Any… |
The state of the component |
predecessor |
Any |
The output of the component on the last event processed |
returned |
Stream -> ComposerHandle |
The logic of the component which processes the stream and returns to compose a handle to the parameters and output of the form f => f(updatedParameters)(output) . Using this handle, compose provides the parameters and predecessor to the component on the next event, and injects the ouput into the template |
Template:: Component... -> Display
Parameter |
Type |
Description |
components |
Component… |
A series of components whose output values are passed as arguments to the template |
compose:: (Template, Component...) -> Composer
Parameter |
Type |
Description |
template |
Template |
The template organizing the display |
reactiveComponents |
Component… |
A sequence of components parameterizing the template |
show:: Display -> () -> Composer -> Process
Parameter |
Type |
Description |
render |
Display -> () |
The render function returned by renderer |
|
|
|
Example: |
|
|
const { continuation, forget, later, now, Source, StreamerTest, value } = require('@acransac/streamer');
const { atom, compose, renderer, show } = require('@acransac/terminal');
const [render, terminate] = renderer();
const loop = async (stream) => {
if (value(now(stream)) === "end") {
return terminate();
}
else {
return loop(await continuation(now(stream))(forget(await later(stream))));
}
};
const template = component => atom(component);
const component = noParameters => predecessor => stream => {
const processed = predecessor ? predecessor : "";
if (value(now(stream)) === "end") {
return f => f(noParameters)(processed);
}
else {
return f => f(noParameters)(`${processed}${value(now(stream))}`);
}
};
Source.from(StreamerTest.emitSequence(["a", "b", "c", "end"], 1000), "onevent")
.withDownstream(async (stream) => loop(await show(render)(compose(template, component))(stream)));
$ node example.js
Test The Display
TerminalTest.reviewDisplays
introduces testing a sequence of displays. A display test is created with makeTestableInertDisplay
or makeTestableReactiveDisplay
. When run, a script executing this review accepts one command line option: look
shows the displays in turn so that they can be visually checked. save
writes the characters and escape sequences of the displays to files so that they can be programmatically verified. control
runs the actual test, comparing the generated displays to the control files and logging the successes and failures. It is the default option:
TerminalTest.reviewDisplays:: ([TestableDisplay], Maybe<String>) -> ()
Parameter |
Type |
Description |
testableDisplays |
[TestableDisplay] |
An array of testable displays, whether inert or reactive |
testSuiteName |
Maybe\ |
The name of the test suite that appears in logs. Default: Test Suite |
TerminalTest.makeTestableInertDisplay:: (() -> Display, String) -> TestableDisplay
Parameter |
Type |
Description |
display |
() -> Display |
A deferred inert display |
testName |
String |
The name of the test that appears in logs |
TerminalTest.makeTestableReactiveDisplay:: (ReactiveDisplayTest, String, Maybe<TestInitializer>) -> TestableDisplay
Parameter |
Type |
Description |
produceDisplay |
ReactiveDisplayTest |
The test function displaying what is to be checked. Its signature depends on the setup done by the initializer. By default, it should provide a placeholder for the render function as first argument, and then a placeholder for the continuation of the test suite. The test runner provides the actual functions when executing |
testName |
String |
The name of the test that appears in logs |
init |
Maybe\ |
Logic to execute before running the test. Default: calls renderer |
TestInitializer:: (Stream.Writable, ReactiveDisplayTest, () -> ()) -> ()
Parameter |
Type |
Description |
displayTarget |
Stream.Writable |
A reference to the target for writing the characters and escape sequences of the display. It could be a file or the standard output depending on the mode of the test runner |
test |
ReactiveDisplayTest |
A reference to the test function to call once the initialization is done |
finish |
() -> () |
A reference to the continuation of the test to call once the test is done |
|
|
|
Examples: |
|
|
1.
const { continuation, forget, later, now, Source, StreamerTest, value } = require('@acransac/streamer');
const { atom, compose, show, TerminalTest } = require('@acransac/terminal');
function reactiveDisplayTest(render, finish) {
const loop = async (stream) => {
if (value(now(stream)) === "end") {
return finish();
}
else {
return loop(await continuation(now(stream))(forget(await later(stream))));
}
};
const template = component => atom(component);
const component = noParameters => predecessor => stream => {
const processed = predecessor ? predecessor : "";
if (value(now(stream)) === "end") {
return f => f(noParameters)(processed);
}
else {
return f => f(noParameters)(`${processed}${value(now(stream))}`);
}
};
Source.from(StreamerTest.emitSequence(["a", "b", "c", "end"], 1000), "onevent")
.withDownstream(async (stream) => loop(await show(render)(compose(template, component))(stream)));
}
TerminalTest.reviewDisplays([
TerminalTest.makeTestableInertDisplay(() => atom("abc"), "Inert Display"),
TerminalTest.makeTestableReactiveDisplay(reactiveDisplayTest, "Reactive Display")
], "Example Tests");
$ node example.js look
$ node example.js save
$ node example.js control
--------------------
Example Tests:
2 / 2 test(s) passed
--------------------
2.
const { continuation, forget, later, now, Source, StreamerTest, value } = require('@acransac/streamer');
const { atom, compose, renderer, show, TerminalTest } = require('@acransac/terminal');
function reactiveDisplayTest(render, finish) {
const loop = async (stream) => {
if (value(now(stream)) === "end") {
return finish();
}
else {
return loop(await continuation(now(stream))(forget(await later(stream))));
}
};
const template = component => atom(component);
const component = noParameters => predecessor => stream => {
const processed = predecessor ? predecessor : "";
if (value(now(stream)) === "end") {
return f => f(noParameters)(processed);
}
else {
return f => f(noParameters)(`${processed}${value(now(stream))}`);
}
};
return async (stream) => loop(await show(render)(compose(template, component))(stream));
}
function testInitializer(displayTarget, test, finish) {
const [render, terminate] = renderer(displayTarget);
const conclude = () => {
terminate();
return finish();
}
Source.from(StreamerTest.emitSequence(["a", "b", "c", "end"], 1000), "onevent")
.withDownstream(async (stream) => test(render, conclude)(stream));
}
TerminalTest.reviewDisplays([
TerminalTest.makeTestableReactiveDisplay(reactiveDisplayTest,
"Reactive Display With Initializer",
testInitializer)
], "Example Tests");
$ node example.js look
$ node example.js save
$ node example.js control
--------------------
Example Tests:
1 / 1 test(s) passed
--------------------