UNPKG

5.49 kBPlain TextView Raw
1// tslint:disable-next-line
2import 'mocha';
3import * as assert from 'assert';
4import * as sinon from 'sinon';
5import {setupReusable} from '../src/index';
6import xs, {Stream} from 'xstream';
7
8describe('setupReusable', function() {
9 it('should be a function', function() {
10 assert.strictEqual(typeof setupReusable, 'function');
11 });
12
13 it('should throw if argument is not object', function() {
14 assert.throws(() => {
15 (setupReusable as any)('not a function');
16 }, /Argument given to setupReusable must be an object with driver/i);
17 });
18
19 it('should throw if argument is an empty object', function() {
20 assert.throws(() => {
21 (setupReusable as any)({});
22 }, /Argument given to setupReusable must be an object with at least one/i);
23 });
24
25 it('should return engine with sources and run', function() {
26 function app(ext: any): any {
27 return {
28 other: ext.other.take(1).startWith('a'),
29 };
30 }
31 function driver() {
32 return xs.of('b');
33 }
34 const {sources, run} = setupReusable({other: driver});
35 assert.strictEqual(typeof sources, 'object');
36 assert.notStrictEqual(typeof sources.other, 'undefined');
37 assert.notStrictEqual(sources.other, null);
38 assert.strictEqual(typeof sources.other.addListener, 'function');
39 assert.strictEqual(typeof run, 'function');
40 });
41
42 it('should return an engine, which we can run and dispose', function() {
43 const sandbox = sinon.createSandbox();
44 const spy = sandbox.spy();
45
46 type NiceSources = {
47 other: Stream<string>;
48 };
49 type NiceSinks = {
50 other: Stream<string>;
51 };
52
53 function app(sources: NiceSources): NiceSinks {
54 return {
55 other: sources.other.take(1).startWith('a'),
56 };
57 }
58
59 function driver(sink: Stream<string>) {
60 return xs.of('b').debug(spy);
61 }
62
63 const engine = setupReusable({other: driver});
64 const sinks = app(engine.sources);
65 const dispose = engine.run(sinks);
66 assert.strictEqual(typeof dispose, 'function');
67 sinon.assert.calledOnce(spy);
68 dispose();
69 });
70
71 it('should allow reusing drivers for many apps', function(done) {
72 const sandbox = sinon.createSandbox();
73 const spy1 = sandbox.spy();
74 const spy2 = sandbox.spy();
75
76 type NiceSources = {
77 other: Stream<string>;
78 };
79 type NiceSinks = {
80 other: Stream<string>;
81 };
82
83 function app1(sources: NiceSources): NiceSinks {
84 return {
85 other: sources.other.mapTo('a').debug(spy1),
86 };
87 }
88
89 function app2(sources: NiceSources): NiceSinks {
90 return {
91 other: sources.other.mapTo('x').debug(spy2),
92 };
93 }
94
95 let sinkCompleted = 0;
96 function driver(sink: Stream<string>) {
97 sink.addListener({
98 complete: () => {
99 sinkCompleted++;
100 done(
101 new Error('complete should not be called before engine is before')
102 );
103 },
104 });
105 return xs.of('b');
106 }
107
108 const engine = setupReusable({other: driver});
109
110 const dispose1 = engine.run(app1(engine.sources));
111 sinon.assert.calledOnce(spy1);
112 sinon.assert.calledWithExactly(spy1, 'a');
113 sandbox.restore();
114 dispose1();
115
116 const dispose2 = engine.run(app2(engine.sources));
117 sinon.assert.calledOnce(spy2);
118 sinon.assert.calledWithExactly(spy2, 'x');
119 dispose2();
120 assert.strictEqual(sinkCompleted, 0);
121 done();
122 });
123
124 it('should allow disposing the engine, stopping reusability', function(done) {
125 const sandbox = sinon.createSandbox();
126 const spy = sandbox.spy();
127
128 type NiceSources = {
129 other: Stream<string>;
130 };
131 type NiceSinks = {
132 other: Stream<string>;
133 };
134
135 function app(sources: NiceSources): NiceSinks {
136 return {
137 other: sources.other.mapTo('a').debug(spy),
138 };
139 }
140
141 let sinkCompleted = 0;
142 function driver(sink: Stream<string>) {
143 sink.addListener({
144 complete: () => {
145 sinkCompleted++;
146 },
147 });
148 return xs.of('b');
149 }
150
151 const engine = setupReusable({other: driver});
152
153 engine.run(app(engine.sources));
154 sinon.assert.calledOnce(spy);
155 sinon.assert.calledWithExactly(spy, 'a');
156 sandbox.restore();
157 engine.dispose();
158 assert.strictEqual(sinkCompleted, 1);
159 done();
160 });
161
162 it('should report errors from main() in the console', function(done) {
163 const sandbox = sinon.createSandbox();
164 sandbox.stub(console, 'error');
165
166 function main(sources: any): any {
167 return {
168 other: sources.other
169 .take(1)
170 .startWith('a')
171 .map(() => {
172 throw new Error('malfunction');
173 }),
174 };
175 }
176 function driver(sink: Stream<any>) {
177 sink.addListener({
178 next: () => {},
179 error: (err: any) => {},
180 });
181 return xs.of('b');
182 }
183
184 let caught = false;
185 const engine = setupReusable({other: driver});
186 try {
187 const sinks = main(engine.sources);
188 engine.run(sinks);
189 } catch (e) {
190 caught = true;
191 }
192 setTimeout(() => {
193 sinon.assert.calledOnce(console.error as any);
194 sinon.assert.calledWithExactly(
195 console.error as any,
196 sinon.match((err: any) => err.message === 'malfunction')
197 );
198
199 // Should be false because the error was already reported in the console.
200 // Otherwise we would have double reporting of the error.
201 assert.strictEqual(caught, false);
202
203 sandbox.restore();
204 done();
205 }, 80);
206 });
207});