UNPKG

6.9 kBPlain TextView Raw
1// tslint:disable-next-line
2import 'mocha';
3import * as assert from 'assert';
4import {setup, Driver} from '../src/index';
5import xs, {Stream} from 'xstream';
6import concat from 'xstream/extra/concat';
7import delay from 'xstream/extra/delay';
8
9describe('setup', function() {
10 it('should be a function', function() {
11 assert.strictEqual(typeof setup, 'function');
12 });
13
14 it('should throw if first argument is not a function', function() {
15 assert.throws(() => {
16 (setup as any)('not a function');
17 }, /First argument given to Cycle must be the 'main' function/i);
18 });
19
20 it('should throw if second argument is not an object', function() {
21 assert.throws(() => {
22 (setup as any)(() => {}, 'not an object');
23 }, /Second argument given to Cycle must be an object with driver functions/i);
24 });
25
26 it('should throw if second argument is an empty object', function() {
27 assert.throws(() => {
28 (setup as any)(() => {}, {});
29 }, /Second argument given to Cycle must be an object with at least one/i);
30 });
31
32 it('should allow to have a driver that takes a union as input', function() {
33 function app(so: {drv: Stream<string>}) {
34 return {
35 drv: xs.of('foo'),
36 };
37 }
38
39 const {sinks, sources} = setup(app, {
40 drv: (s: Stream<string | number>) => xs.of('foo'),
41 });
42 });
43
44 it('should allow to not use all sources in main', function() {
45 function app(so: {first: Stream<string>}) {
46 return {
47 first: xs.of('test'),
48 second: xs.of('string'),
49 };
50 }
51 function app2() {
52 return {second: xs.of('test')};
53 }
54 function driver(sink: Stream<string>) {
55 return xs.of('answer');
56 }
57 const {sinks, sources} = setup(app, {first: driver, second: driver});
58 const {sinks: sinks2, sources: sources2} = setup(app2, {
59 first: driver,
60 second: driver,
61 });
62
63 assert.strictEqual(typeof sinks, 'object');
64 assert.strictEqual(typeof sinks.second.addListener, 'function');
65 assert.strictEqual(typeof sinks2, 'object');
66 assert.strictEqual(typeof sinks2.second.addListener, 'function');
67 });
68
69 it('should return sinks object and sources object', function() {
70 function app(ext: {other: Stream<string>}) {
71 return {
72 other: ext.other.take(1).startWith('a'),
73 };
74 }
75 function driver() {
76 return xs.of('b');
77 }
78 const {sinks, sources} = setup(app, {other: driver});
79 assert.strictEqual(typeof sinks, 'object');
80 assert.strictEqual(typeof sinks.other.addListener, 'function');
81 assert.strictEqual(typeof sources, 'object');
82 assert.notStrictEqual(typeof sources.other, 'undefined');
83 assert.notStrictEqual(sources.other, null);
84 assert.strictEqual(typeof sources.other.addListener, 'function');
85 });
86
87 it('should type-check keyof sources and sinks in main and drivers', function() {
88 type Sources = {
89 str: Stream<string>;
90 obj: Stream<object>;
91 };
92
93 function app(sources: Sources) {
94 return {
95 str: sources.str.take(1).startWith('a'), // good
96 // str: sources.obj.mapTo('good'), // good
97 // strTYPO: sources.str.take(1).startWith('a'), // bad
98 // str: xs.of(123), // bad
99 num: xs.of(100), // good
100 // numTYPO: xs.of(100), // bad
101 // num: xs.of('BAD TYPE'), // bad
102 };
103 }
104
105 const stringDriver: Driver<Stream<string>, Stream<string>> = (
106 sink: Stream<string>
107 ) => xs.of('b');
108
109 const numberWriteOnlyDriver: Driver<Stream<number>, void> = (
110 sink: Stream<number>
111 ) => {};
112
113 const objectReadOnlyDriver: Driver<void, Stream<object>> = () => xs.of({});
114
115 setup(app, {
116 str: stringDriver,
117 num: numberWriteOnlyDriver,
118 obj: objectReadOnlyDriver,
119 });
120 });
121
122 it('should type-check keyof sources and sinks, supporting interfaces', function() {
123 interface Sources {
124 str: Stream<string>;
125 obj: Stream<object>;
126 }
127
128 interface Sinks {
129 str: Stream<string>;
130 num: Stream<number>;
131 }
132
133 function app(sources: Sources): Sinks {
134 return {
135 str: sources.str.take(1).startWith('a'), // good
136 // str: sources.obj.mapTo('good'), // good
137 // strTYPO: sources.str.take(1).startWith('a'), // bad
138 // str: xs.of(123), // bad
139 num: xs.of(100), // good
140 // numTYPO: xs.of(100), // bad
141 // num: xs.of('BAD TYPE'), // bad
142 };
143 }
144
145 const stringDriver: Driver<Stream<string>, Stream<string>> = (
146 sink: Stream<string>
147 ) => xs.of('b');
148
149 const numberWriteOnlyDriver: Driver<Stream<number>, void> = (
150 sink: Stream<number>
151 ) => {};
152
153 const objectReadOnlyDriver: Driver<void, Stream<object>> = () => xs.of({});
154
155 setup(app, {
156 str: stringDriver,
157 num: numberWriteOnlyDriver,
158 obj: objectReadOnlyDriver,
159 });
160 });
161
162 it('should type-check and allow more drivers than sinks', function() {
163 type Sources = {
164 str: Stream<string>;
165 num: Stream<number>;
166 obj: Stream<object>;
167 };
168
169 function app(sources: Sources) {
170 return {};
171 }
172
173 function stringDriver(sink: Stream<string>) {
174 return xs.of('b');
175 }
176
177 const numberDriver = (sink: Stream<number>) => xs.of(100);
178
179 const objectReadOnlyDriver = () => xs.of({});
180
181 setup(app, {
182 str: stringDriver,
183 num: numberDriver,
184 obj: objectReadOnlyDriver,
185 });
186 });
187
188 it('should return a run() which in turn returns a dispose()', function(done) {
189 type TestSources = {
190 other: Stream<number>;
191 };
192
193 function app(_sources: TestSources) {
194 return {
195 other: concat(
196 _sources.other
197 .take(6)
198 .map(String)
199 .startWith('a'),
200 xs.never()
201 ),
202 };
203 }
204
205 function driver(sink: Stream<string>) {
206 return sink.map(x => x.charCodeAt(0)).compose(delay(1));
207 }
208
209 const {sources, run} = setup(app, {other: driver});
210
211 let dispose: any;
212 sources.other.addListener({
213 next: x => {
214 assert.strictEqual(x, 97);
215 dispose(); // will trigger this listener's complete
216 },
217 error: done,
218 complete: done,
219 });
220 dispose = run();
221 });
222
223 it('should not work after has been disposed', function(done) {
224 type MySources = {
225 other: Stream<string>;
226 };
227
228 function app(_sources: MySources) {
229 return {other: xs.periodic(100).map(i => i + 1)};
230 }
231 function driver(num$: Stream<number>): Stream<string> {
232 return num$.map(num => 'x' + num);
233 }
234
235 const {sources, run} = setup(app, {
236 other: driver,
237 });
238
239 let dispose: any;
240 sources.other.addListener({
241 next: x => {
242 assert.notStrictEqual(x, 'x3');
243 if (x === 'x2') {
244 dispose(); // will trigger this listener's complete
245 }
246 },
247 error: done,
248 complete: done,
249 });
250 dispose = run();
251 });
252});