##// END OF EJS Templates
more tests
cin -
r77:04efbdad7679 default
parent child
Show More
@@ -1,33 +1,33
1 HISTORY
1 HISTORY
2 =======
2 =======
3
3
4 1.2.17
4 1.2.17
5 ------
5 ------
6
6
7 Bug fixes
7 Bug fixes
8
8
9 1.2.16
9 1.2.16
10 ------
10 ------
11
11
12 Minor fixes and improvements
12 Minor fixes and improvements
13
13
14 - added `isCancellable` type predicate function to `safe` module
14 - added `isCancellable` type predicate function to `safe` module
15 - `isString, isNumber, isInteger, isPrimitive` are now type predicates
15 - `isString, isNumber, isInteger, isPrimitive` are now type predicates
16
16
17 1.2.0
17 1.2.0
18 -----
18 -----
19
19
20 Major rafactoring, moving to support both browser (rjs) and server (cjs) environments.
20 Major rafactoring, moving to support both browser (rjs) and server (cjs) environments.
21
21
22 - dependency injection container ported to typescript
22 - dependency injection container ported to typescript
23 - sources are split to several sets to provide the ability for the conditional build of the project.
23 - sources are split to several sets to provide the ability for the conditional build of the project.
24
24
25 1.0.1
25 1.0.1
26 -----
26 -----
27
27
28 First release, intorduces the following features
28 First release, intorduces the following features
29
29
30 - `di` - dependency injection container
30 - `di` - dependency injection container
31 - `log` - log4 style logging system
31 - `log` - log4 style logging system
32 - `text` - simple and fast text templating and formatting
32 - `text` - simple and fast text templating and formatting
33 - `Uuid` - uuid generation traits No newline at end of file
33 - `Uuid` - uuid generation traits
@@ -1,456 +1,454
1 import { ICancellable, Constructor } from "./interfaces";
1 import { ICancellable, Constructor } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
2 import { Cancellation } from "./Cancellation";
3
3
4 let _nextOid = 0;
4 let _nextOid = 0;
5 const _oid = typeof Symbol === "function" ?
5 const _oid = typeof Symbol === "function" ?
6 Symbol("__implab__oid__") :
6 Symbol("__implab__oid__") :
7 "__implab__oid__";
7 "__implab__oid__";
8
8
9 export function oid(instance: object): string {
9 export function oid(instance: object): string {
10 if (isNull(instance))
10 if (isNull(instance))
11 return null;
11 return null;
12
12
13 if (_oid in instance)
13 if (_oid in instance)
14 return instance[_oid];
14 return instance[_oid];
15 else
15 else
16 return (instance[_oid] = "oid_" + (++_nextOid));
16 return (instance[_oid] = "oid_" + (++_nextOid));
17 }
17 }
18
18
19 export function argumentNotNull(arg: any, name: string) {
19 export function argumentNotNull(arg: any, name: string) {
20 if (arg === null || arg === undefined)
20 if (arg === null || arg === undefined)
21 throw new Error("The argument " + name + " can't be null or undefined");
21 throw new Error("The argument " + name + " can't be null or undefined");
22 }
22 }
23
23
24 export function argumentNotEmptyString(arg: any, name: string) {
24 export function argumentNotEmptyString(arg: any, name: string) {
25 if (typeof (arg) !== "string" || !arg.length)
25 if (typeof (arg) !== "string" || !arg.length)
26 throw new Error("The argument '" + name + "' must be a not empty string");
26 throw new Error("The argument '" + name + "' must be a not empty string");
27 }
27 }
28
28
29 export function argumentNotEmptyArray(arg: any, name: string) {
29 export function argumentNotEmptyArray(arg: any, name: string) {
30 if (!(arg instanceof Array) || !arg.length)
30 if (!(arg instanceof Array) || !arg.length)
31 throw new Error("The argument '" + name + "' must be a not empty array");
31 throw new Error("The argument '" + name + "' must be a not empty array");
32 }
32 }
33
33
34 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
34 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
35 if (!(arg instanceof type))
35 if (!(arg instanceof type))
36 throw new Error("The argument '" + name + "' type doesn't match");
36 throw new Error("The argument '" + name + "' type doesn't match");
37 }
37 }
38
38
39 export function isNull(val: any) {
39 export function isNull(val: any) {
40 return (val === null || val === undefined);
40 return (val === null || val === undefined);
41 }
41 }
42
42
43 export function isPrimitive(val: any): val is string | number | boolean | undefined | null {
43 export function isPrimitive(val: any): val is string | number | boolean | undefined | null {
44 return (val === null || val === undefined || typeof (val) === "string" ||
44 return (val === null || val === undefined || typeof (val) === "string" ||
45 typeof (val) === "number" || typeof (val) === "boolean");
45 typeof (val) === "number" || typeof (val) === "boolean");
46 }
46 }
47
47
48 export function isInteger(val: any): val is number {
48 export function isInteger(val: any): val is number {
49 return parseInt(val, 10) === val;
49 return parseInt(val, 10) === val;
50 }
50 }
51
51
52 export function isNumber(val: any): val is number {
52 export function isNumber(val: any): val is number {
53 return parseFloat(val) === val;
53 return parseFloat(val) === val;
54 }
54 }
55
55
56 export function isString(val: any): val is string {
56 export function isString(val: any): val is string {
57 return typeof (val) === "string" || val instanceof String;
57 return typeof (val) === "string" || val instanceof String;
58 }
58 }
59
59
60 export function isPromise(val: any): val is PromiseLike<any> {
60 export function isPromise(val: any): val is PromiseLike<any> {
61 return val && typeof val.then === "function";
61 return val && typeof val.then === "function";
62 }
62 }
63
63
64 export function isCancellable(val: any): val is ICancellable {
64 export function isCancellable(val: any): val is ICancellable {
65 return val && typeof val.cancel === "function";
65 return val && typeof val.cancel === "function";
66 }
66 }
67
67
68 export function isNullOrEmptyString(val: any): val is string | null | undefined {
68 export function isNullOrEmptyString(val: any): val is string | null | undefined {
69 if (val === null || val === undefined ||
69 if (val === null || val === undefined ||
70 ((typeof (val) === "string" || val instanceof String) && val.length === 0))
70 ((typeof (val) === "string" || val instanceof String) && val.length === 0))
71 return true;
71 return true;
72 }
72 }
73
73
74 export function isNotEmptyArray(arg: any): arg is Array<any> {
74 export function isNotEmptyArray(arg: any): arg is Array<any> {
75 return (arg instanceof Array && arg.length > 0);
75 return (arg instanceof Array && arg.length > 0);
76 }
76 }
77
77
78 function _isStrictMode() {
78 function _isStrictMode() {
79 return !this;
79 return !this;
80 }
80 }
81
81
82 function _getNonStrictGlobal() {
82 function _getNonStrictGlobal() {
83 return this;
83 return this;
84 }
84 }
85
85
86 export function getGlobal() {
86 export function getGlobal() {
87 // in es3 we can't use indirect call to eval, since it will
87 // in es3 we can't use indirect call to eval, since it will
88 // be executed in the current call context.
88 // be executed in the current call context.
89 if (!_isStrictMode()) {
89 if (!_isStrictMode()) {
90 return _getNonStrictGlobal();
90 return _getNonStrictGlobal();
91 } else {
91 } else {
92 // tslint:disable-next-line:no-eval
92 // tslint:disable-next-line:no-eval
93 return eval.call(null, "this");
93 return eval.call(null, "this");
94 }
94 }
95 }
95 }
96
96
97 export function get(member: string, context?: object) {
97 export function get(member: string, context?: object) {
98 argumentNotEmptyString(member, "member");
98 argumentNotEmptyString(member, "member");
99 let that = context || getGlobal();
99 let that = context || getGlobal();
100 const parts = member.split(".");
100 const parts = member.split(".");
101 for (const m of parts) {
101 for (const m of parts) {
102 if (!m)
102 if (!m)
103 continue;
103 continue;
104 if (isNull(that = that[m]))
104 if (isNull(that = that[m]))
105 break;
105 break;
106 }
106 }
107 return that;
107 return that;
108 }
108 }
109
109
110 /**
110 /**
111 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
111 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
112 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
112 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
113 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
113 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
114 *
114 *
115 * @param {Array | Object} obj массив элСмСнтов для просмотра
115 * @param {Array | Object} obj массив элСмСнтов для просмотра
116 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
116 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
117 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
117 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
118 * <c>this</c> Π² <c>cb</c>.
118 * <c>this</c> Π² <c>cb</c>.
119 * @returns Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π²Ρ‹Π·ΠΎΠ²Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ <c>cb</c>, Π»ΠΈΠ±ΠΎ <c>undefined</c>
119 * @returns Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π²Ρ‹Π·ΠΎΠ²Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ <c>cb</c>, Π»ΠΈΠ±ΠΎ <c>undefined</c>
120 * Ссли достигнут ΠΊΠΎΠ½Π΅Ρ† массива.
120 * Ссли достигнут ΠΊΠΎΠ½Π΅Ρ† массива.
121 */
121 */
122 export function each(obj, cb, thisArg?) {
122 export function each(obj, cb, thisArg?) {
123 argumentNotNull(cb, "cb");
123 argumentNotNull(cb, "cb");
124 if (obj instanceof Array) {
124 if (obj instanceof Array) {
125 for (let i = 0; i < obj.length; i++) {
125 for (let i = 0; i < obj.length; i++) {
126 const x = cb.call(thisArg, obj[i], i);
126 const x = cb.call(thisArg, obj[i], i);
127 if (x !== undefined)
127 if (x !== undefined)
128 return x;
128 return x;
129 }
129 }
130 } else {
130 } else {
131 const keys = Object.keys(obj);
131 const keys = Object.keys(obj);
132 for (const k of keys) {
132 for (const k of keys) {
133 const x = cb.call(thisArg, obj[k], k);
133 const x = cb.call(thisArg, obj[k], k);
134 if (x !== undefined)
134 if (x !== undefined)
135 return x;
135 return x;
136 }
136 }
137 }
137 }
138 }
138 }
139
139
140 /** Copies property values from a source object to the destination and returns
140 /** Copies property values from a source object to the destination and returns
141 * the destination onject.
141 * the destination onject.
142 *
142 *
143 * @param dest The destination object into which properties from the source
143 * @param dest The destination object into which properties from the source
144 * object will be copied.
144 * object will be copied.
145 * @param source The source of values which will be copied to the destination
145 * @param source The source of values which will be copied to the destination
146 * object.
146 * object.
147 * @param template An optional parameter specifies which properties should be
147 * @param template An optional parameter specifies which properties should be
148 * copied from the source and how to map them to the destination. If the
148 * copied from the source and how to map them to the destination. If the
149 * template is an array it contains the list of property names to copy from the
149 * template is an array it contains the list of property names to copy from the
150 * source to the destination. In case of object the templates contains the map
150 * source to the destination. In case of object the templates contains the map
151 * where keys are property names in the source and the values are property
151 * where keys are property names in the source and the values are property
152 * names in the destination object. If the template isn't specified then the
152 * names in the destination object. If the template isn't specified then the
153 * own properties of the source are entirely copied to the destination.
153 * own properties of the source are entirely copied to the destination.
154 *
154 *
155 */
155 */
156 export function mixin<T, S>(dest: T, source: S, template?: string[] | object): T & S {
156 export function mixin<T, S>(dest: T, source: S, template?: string[] | object): T & S {
157 argumentNotNull(dest, "to");
157 argumentNotNull(dest, "to");
158 const _res = dest as T & S;
158 const _res = dest as T & S;
159
159
160 if (isPrimitive(source))
160 if (isPrimitive(source))
161 return _res;
161 return _res;
162
162
163 if (template instanceof Array) {
163 if (template instanceof Array) {
164 for (const p of template) {
164 for (const p of template) {
165 if (p in source)
165 if (p in source)
166 _res[p] = source[p];
166 _res[p] = source[p];
167 }
167 }
168 } else if (template) {
168 } else if (template) {
169 const keys = Object.keys(source);
169 const keys = Object.keys(source);
170 for (const p of keys) {
170 for (const p of keys) {
171 if (p in template)
171 if (p in template)
172 _res[template[p]] = source[p];
172 _res[template[p]] = source[p];
173 }
173 }
174 } else {
174 } else {
175 const keys = Object.keys(source);
175 const keys = Object.keys(source);
176 for (const p of keys)
176 for (const p of keys)
177 _res[p] = source[p];
177 _res[p] = source[p];
178 }
178 }
179
179
180 return _res;
180 return _res;
181 }
181 }
182
182
183 /** Wraps the specified function to emulate an asynchronous execution.
183 /** Wraps the specified function to emulate an asynchronous execution.
184 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
184 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
185 * @param{Function|String} fn [Required] Function wich will be wrapped.
185 * @param{Function|String} fn [Required] Function wich will be wrapped.
186 */
186 */
187 export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike<any> {
187 export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike<any> {
188 let fn = _fn;
188 let fn = _fn;
189
189
190 if (arguments.length === 2 && !(fn instanceof Function))
190 if (arguments.length === 2 && !(fn instanceof Function))
191 fn = thisArg[fn];
191 fn = thisArg[fn];
192
192
193 if (fn == null)
193 if (fn == null)
194 throw new Error("The function must be specified");
194 throw new Error("The function must be specified");
195
195
196 function wrapresult(x, e?): PromiseLike<any> {
196 function wrapresult(x, e?): PromiseLike<any> {
197 if (e) {
197 if (e) {
198 return {
198 return {
199 then(cb, eb) {
199 then(cb, eb) {
200 try {
200 try {
201 return eb ? wrapresult(eb(e)) : this;
201 return eb ? wrapresult(eb(e)) : this;
202 } catch (e2) {
202 } catch (e2) {
203 return wrapresult(null, e2);
203 return wrapresult(null, e2);
204 }
204 }
205 }
205 }
206 };
206 };
207 } else {
207 } else {
208 if (x && x.then)
208 if (x && x.then)
209 return x;
209 return x;
210 return {
210 return {
211 then(cb) {
211 then(cb) {
212 try {
212 try {
213 return cb ? wrapresult(cb(x)) : this;
213 return cb ? wrapresult(cb(x)) : this;
214 } catch (e2) {
214 } catch (e2) {
215 return wrapresult(e2);
215 return wrapresult(e2);
216 }
216 }
217 }
217 }
218 };
218 };
219 }
219 }
220 }
220 }
221
221
222 return (...args) => {
222 return (...args) => {
223 try {
223 try {
224 return wrapresult(fn.apply(thisArg, args));
224 return wrapresult(fn.apply(thisArg, args));
225 } catch (e) {
225 } catch (e) {
226 return wrapresult(null, e);
226 return wrapresult(null, e);
227 }
227 }
228 };
228 };
229 }
229 }
230
230
231 type _AnyFn = (...args) => any;
231 type _AnyFn = (...args) => any;
232
232
233 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
233 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
234 let method;
234 let method;
235
235
236 if (!(_method instanceof Function)) {
236 if (!(_method instanceof Function)) {
237 argumentNotNull(target, "target");
237 argumentNotNull(target, "target");
238 method = target[_method];
238 method = target[_method];
239 if (!(method instanceof Function))
239 if (!(method instanceof Function))
240 throw new Error("'method' argument must be a Function or a method name");
240 throw new Error("'method' argument must be a Function or a method name");
241 } else {
241 } else {
242 method = _method;
242 method = _method;
243 }
243 }
244
244
245 return (...args) => {
245 return (...args) => {
246 return method.apply(target, args);
246 return method.apply(target, args);
247 };
247 };
248 }
248 }
249
249
250 export function delay(timeMs: number, ct = Cancellation.none) {
250 export function delay(timeMs: number, ct = Cancellation.none) {
251 ct.throwIfRequested();
251 return new Promise((resolve, reject) => {
252 return new Promise((resolve, reject) => {
252 if (ct.isRequested()) {
253 const h = ct.register(e => {
253 ct.register(reject);
254 clearTimeout(id);
254 } else {
255 reject(e);
255 const h = ct.register(e => {
256 // we don't nedd to unregister h, since ct is already disposed
256 clearTimeout(id);
257 });
257 reject(e);
258 const id = setTimeout(() => {
258 // we don't nedd to unregister h, since ct is already disposed
259 h.destroy();
259 });
260 resolve();
260 const id = setTimeout(() => {
261 }, timeMs);
261 h.destroy();
262
262 resolve();
263 }, timeMs);
264 }
265 });
263 });
266 }
264 }
267
265
268 /**
266 /**
269 * Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ ΡƒΠΊΠ°Π·Π°Π½Π½ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΈ сохраняСт
267 * Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ ΡƒΠΊΠ°Π·Π°Π½Π½ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΈ сохраняСт
270 * Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π΅Π½Π½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π² массивС Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ².
268 * Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π΅Π½Π½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π² массивС Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ².
271 *
269 *
272 * @remarks cb ΠΌΠΎΠΆΠ΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒΡΡ асинхронно, ΠΏΡ€ΠΈ этом ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚
270 * @remarks cb ΠΌΠΎΠΆΠ΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒΡΡ асинхронно, ΠΏΡ€ΠΈ этом ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚
273 * Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ΄Π½Π° опСрация.
271 * Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ΄Π½Π° опСрация.
274 *
272 *
275 * @async
273 * @async
276 */
274 */
277 export function pmap(items, cb) {
275 export function pmap(items, cb) {
278 argumentNotNull(cb, "cb");
276 argumentNotNull(cb, "cb");
279
277
280 if (isPromise(items))
278 if (isPromise(items))
281 return items.then(data => pmap(data, cb));
279 return items.then(data => pmap(data, cb));
282
280
283 if (isNull(items) || !items.length)
281 if (isNull(items) || !items.length)
284 return items;
282 return items;
285
283
286 let i = 0;
284 let i = 0;
287 const result = [];
285 const result = [];
288
286
289 function next() {
287 function next() {
290 let r;
288 let r;
291 let ri;
289 let ri;
292
290
293 function chain(x) {
291 function chain(x) {
294 result[ri] = x;
292 result[ri] = x;
295 return next();
293 return next();
296 }
294 }
297
295
298 while (i < items.length) {
296 while (i < items.length) {
299 r = cb(items[i], i);
297 r = cb(items[i], i);
300 ri = i;
298 ri = i;
301 i++;
299 i++;
302 if (isPromise(r)) {
300 if (isPromise(r)) {
303 return r.then(chain);
301 return r.then(chain);
304 } else {
302 } else {
305 result[ri] = r;
303 result[ri] = r;
306 }
304 }
307 }
305 }
308 return result;
306 return result;
309 }
307 }
310
308
311 return next();
309 return next();
312 }
310 }
313
311
314 export function pfor(items, cb) {
312 export function pfor(items, cb) {
315 argumentNotNull(cb, "cb");
313 argumentNotNull(cb, "cb");
316
314
317 if (isPromise(items))
315 if (isPromise(items))
318 return items.then(data => {
316 return items.then(data => {
319 return pmap(data, cb);
317 return pmap(data, cb);
320 });
318 });
321
319
322 if (isNull(items) || !items.length)
320 if (isNull(items) || !items.length)
323 return items;
321 return items;
324
322
325 let i = 0;
323 let i = 0;
326
324
327 function next() {
325 function next() {
328 while (i < items.length) {
326 while (i < items.length) {
329 const r = cb(items[i], i);
327 const r = cb(items[i], i);
330 i++;
328 i++;
331 if (isPromise(r))
329 if (isPromise(r))
332 return r.then(next);
330 return r.then(next);
333 }
331 }
334 }
332 }
335
333
336 return next();
334 return next();
337 }
335 }
338
336
339 export function first<T>(sequence: ArrayLike<T>): T;
337 export function first<T>(sequence: ArrayLike<T>): T;
340 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
338 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
341 export function first<T>(
339 export function first<T>(
342 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
340 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
343 cb: (x: T) => void,
341 cb: (x: T) => void,
344 err?: (x: Error) => void
342 err?: (x: Error) => void
345 ): void;
343 ): void;
346 /**
344 /**
347 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
345 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
348 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
346 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
349 *
347 *
350 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
348 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
351 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
349 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
352 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
350 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
353 * нС массив
351 * нС массив
354 *
352 *
355 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
353 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
356 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
354 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
357 * @async
355 * @async
358 */
356 */
359 export function first<T>(
357 export function first<T>(
360 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
358 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
361 cb?: (x: T) => void,
359 cb?: (x: T) => void,
362 err?: (x: Error) => void
360 err?: (x: Error) => void
363 ) {
361 ) {
364 if (isPromise(sequence)) {
362 if (isPromise(sequence)) {
365 return sequence.then(res => first(res, cb, err));
363 return sequence.then(res => first(res, cb, err));
366 } else if (sequence && "length" in sequence) {
364 } else if (sequence && "length" in sequence) {
367 if (sequence.length === 0) {
365 if (sequence.length === 0) {
368 if (err)
366 if (err)
369 return err(new Error("The sequence is empty"));
367 return err(new Error("The sequence is empty"));
370 else
368 else
371 throw new Error("The sequence is empty");
369 throw new Error("The sequence is empty");
372 } else if (cb) {
370 } else if (cb) {
373 cb(sequence[0]);
371 cb(sequence[0]);
374 } else {
372 } else {
375 return sequence[0];
373 return sequence[0];
376 }
374 }
377 } else {
375 } else {
378 if (err)
376 if (err)
379 err(new Error("The sequence is required"));
377 err(new Error("The sequence is required"));
380 else
378 else
381 throw new Error("The sequence is required");
379 throw new Error("The sequence is required");
382 }
380 }
383 }
381 }
384
382
385 export function firstWhere<T>(
383 export function firstWhere<T>(
386 sequence: ArrayLike<T>,
384 sequence: ArrayLike<T>,
387 predicate: (x: T) => boolean
385 predicate: (x: T) => boolean
388 ): T;
386 ): T;
389 export function firstWhere<T>(
387 export function firstWhere<T>(
390 sequence: PromiseLike<ArrayLike<T>>,
388 sequence: PromiseLike<ArrayLike<T>>,
391 predicate: (x: T) => boolean
389 predicate: (x: T) => boolean
392 ): PromiseLike<T>;
390 ): PromiseLike<T>;
393 export function firstWhere<T>(
391 export function firstWhere<T>(
394 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
392 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
395 predicate: (x: T) => boolean,
393 predicate: (x: T) => boolean,
396 cb: (x: T) => void,
394 cb: (x: T) => void,
397 err?: (x: Error) => void
395 err?: (x: Error) => void
398 ): void;
396 ): void;
399
397
400 export function firstWhere<T>(
398 export function firstWhere<T>(
401 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
399 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
402 predicate?: (x: T) => boolean,
400 predicate?: (x: T) => boolean,
403 cb?: (x: T) => any,
401 cb?: (x: T) => any,
404 err?: (x: Error) => any
402 err?: (x: Error) => any
405 ) {
403 ) {
406 if (isPromise(sequence)) {
404 if (isPromise(sequence)) {
407 return sequence.then(res => firstWhere(res, predicate, cb, err));
405 return sequence.then(res => firstWhere(res, predicate, cb, err));
408 } else if (sequence && "length" in sequence) {
406 } else if (sequence && "length" in sequence) {
409 if (sequence.length === 0) {
407 if (sequence.length === 0) {
410 if (err)
408 if (err)
411 err(new Error("The sequence is empty"));
409 err(new Error("The sequence is empty"));
412 else
410 else
413 throw new Error("The sequence is empty");
411 throw new Error("The sequence is empty");
414 } else {
412 } else {
415 if (!predicate) {
413 if (!predicate) {
416 return cb ? cb(sequence[0]) && void (0) : sequence[0];
414 return cb ? cb(sequence[0]) && void (0) : sequence[0];
417 } else {
415 } else {
418 for (let i = 0; i < sequence.length; i++) {
416 for (let i = 0; i < sequence.length; i++) {
419 const v = sequence[i];
417 const v = sequence[i];
420 if (predicate(v))
418 if (predicate(v))
421 return cb ? cb(v) : v;
419 return cb ? cb(v) : v;
422 }
420 }
423 if (err)
421 if (err)
424 err(new Error("The sequence doesn't contain matching items"));
422 err(new Error("The sequence doesn't contain matching items"));
425 else
423 else
426 throw new Error("The sequence doesn't contain matching items");
424 throw new Error("The sequence doesn't contain matching items");
427 }
425 }
428 }
426 }
429 } else {
427 } else {
430 if (err)
428 if (err)
431 err(new Error("The sequence is required"));
429 err(new Error("The sequence is required"));
432 else
430 else
433 throw new Error("The sequence is required");
431 throw new Error("The sequence is required");
434 }
432 }
435 }
433 }
436
434
437 export function destroy(d: any) {
435 export function destroy(d: any) {
438 if (d && "destroy" in d)
436 if (d && "destroy" in d)
439 d.destroy();
437 d.destroy();
440 }
438 }
441
439
442 /**
440 /**
443 * Used to mark that the async operation isn't awaited intentionally.
441 * Used to mark that the async operation isn't awaited intentionally.
444 * @param p The promise which represents the async operation.
442 * @param p The promise which represents the async operation.
445 */
443 */
446 export function nowait(p: Promise<any>) {
444 export function nowait(p: Promise<any>) {
447 }
445 }
448
446
449 /** represents already destroyed object.
447 /** represents already destroyed object.
450 */
448 */
451 export const destroyed = {
449 export const destroyed = {
452 /** Calling to this method doesn't affect anything, noop.
450 /** Calling to this method doesn't affect anything, noop.
453 */
451 */
454 destroy() {
452 destroy() {
455 }
453 }
456 };
454 };
@@ -1,97 +1,96
1 import * as tape from "tape";
1 import * as tape from "tape";
2 import { Cancellation } from "@implab/core/Cancellation";
2 import { Cancellation } from "@implab/core/Cancellation";
3 import { ICancellation } from "@implab/core/interfaces";
3 import { delay } from "@implab/core/safe";
4 import { delay } from "./TestTraits";
5
4
6 tape("standalone cancellation", async t => {
5 tape("standalone cancellation", async t => {
7
6
8 let doCancel: (e) => void;
7 let doCancel: (e) => void;
9
8
10 const ct = new Cancellation(cancel => {
9 const ct = new Cancellation(cancel => {
11 doCancel = cancel;
10 doCancel = cancel;
12 });
11 });
13
12
14 let counter = 0;
13 let counter = 0;
15 const reason = "BILL";
14 const reason = "BILL";
16
15
17 t.true(ct.isSupported(), "Cancellation must be supported");
16 t.true(ct.isSupported(), "Cancellation must be supported");
18 t.false(ct.isRequested(), "Cancellation shouldn't be requested");
17 t.false(ct.isRequested(), "Cancellation shouldn't be requested");
19 ct.throwIfRequested();
18 ct.throwIfRequested();
20 t.pass("The exception shouldn't be thrown unless the cancellation is requested");
19 t.pass("The exception shouldn't be thrown unless the cancellation is requested");
21
20
22 ct.register(() => counter++);
21 ct.register(() => counter++);
23 t.equals(counter, 0, "counter should be zero");
22 t.equals(counter, 0, "counter should be zero");
24
23
25 ct.register(() => counter++).destroy();
24 ct.register(() => counter++).destroy();
26
25
27 doCancel(reason);
26 doCancel(reason);
28
27
29 t.true(ct.isRequested(), "Cancellation should be requested");
28 t.true(ct.isRequested(), "Cancellation should be requested");
30 t.equals(counter, 1, "The registered callback should be triggered");
29 t.equals(counter, 1, "The registered callback should be triggered");
31
30
32 ct.register(() => counter++);
31 ct.register(() => counter++);
33 t.equals(counter, 2, "The callback should be triggered immediately");
32 t.equals(counter, 2, "The callback should be triggered immediately");
34
33
35 let msg;
34 let msg;
36 ct.register(e => msg = e);
35 ct.register(e => msg = e);
37 t.equals(msg, reason, "The cancellation reason should be passed to callback");
36 t.equals(msg, reason, "The cancellation reason should be passed to callback");
38
37
39 try {
38 try {
40 msg = null;
39 msg = null;
41 ct.throwIfRequested();
40 ct.throwIfRequested();
42 t.fail("The exception should be thrown");
41 t.fail("The exception should be thrown");
43 } catch (e) {
42 } catch (e) {
44 msg = e;
43 msg = e;
45 }
44 }
46 t.equals(msg, reason, "The cancellation reason should be catched");
45 t.equals(msg, reason, "The cancellation reason should be catched");
47
46
48 t.end();
47 t.end();
49 });
48 });
50
49
51 tape("async cancellation", async t => {
50 tape("async cancellation", async t => {
52
51
53 const ct = new Cancellation(cancel => {
52 const ct = new Cancellation(cancel => {
54 cancel("STOP!");
53 cancel("STOP!");
55 });
54 });
56
55
57 try {
56 try {
58 await delay(0, ct);
57 await delay(0, ct);
59 t.fail("Should thow the exception");
58 t.fail("Should thow the exception");
60 } catch (e) {
59 } catch (e) {
61 t.equals(e, "STOP!", "Should throw the cancellation reason");
60 t.equals(e, "STOP!", "Should throw the cancellation reason");
62 }
61 }
63
62
64 t.end();
63 t.end();
65 });
64 });
66
65
67 tape("cancel with external event", async t => {
66 tape("cancel with external event", async t => {
68 const ct = new Cancellation(cancel => {
67 const ct = new Cancellation(cancel => {
69 setTimeout(x => cancel("STOP!"), 0);
68 setTimeout(x => cancel("STOP!"), 0);
70 });
69 });
71
70
72 try {
71 try {
73 await delay(10000, ct);
72 await delay(10000, ct);
74 t.fail("Should thow the exception");
73 t.fail("Should thow the exception");
75 } catch (e) {
74 } catch (e) {
76 t.equals(e, "STOP!", "Should throw the cancellation reason");
75 t.equals(e, "STOP!", "Should throw the cancellation reason");
77 }
76 }
78
77
79 t.end();
78 t.end();
80 });
79 });
81
80
82 tape("operation normal flow", async t => {
81 tape("operation normal flow", async t => {
83
82
84 let htimeout;
83 let htimeout;
85 const ct = new Cancellation(cancel => {
84 const ct = new Cancellation(cancel => {
86 htimeout = setTimeout(() => cancel("STOP!"), 1000);
85 htimeout = setTimeout(() => cancel("STOP!"), 1000);
87 });
86 });
88
87
89 try {
88 try {
90 await delay(0, ct);
89 await delay(0, ct);
91 t.pass("Should pass");
90 t.pass("Should pass");
92 } finally {
91 } finally {
93 clearTimeout(htimeout);
92 clearTimeout(htimeout);
94 }
93 }
95
94
96 t.end();
95 t.end();
97 });
96 });
@@ -1,73 +1,73
1 import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
1 import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
2 import * as tape from "tape";
2 import * as tape from "tape";
3 import { TapeWriter, delay } from "./TestTraits";
4 import { Observable } from "@implab/core/Observable";
3 import { Observable } from "@implab/core/Observable";
5 import { IObservable } from "@implab/core/interfaces";
4 import { IObservable } from "@implab/core/interfaces";
5 import { delay } from "@implab/core/safe";
6
6
7 const trace = TraceSource.get("ObservableTests");
7 const trace = TraceSource.get("ObservableTests");
8
8
9 tape("events sequence example", async t => {
9 tape("events sequence example", async t => {
10
10
11 let events: IObservable<number>;
11 let events: IObservable<number>;
12
12
13 const done = new Promise<void>(resolve => {
13 const done = new Promise<void>(resolve => {
14 events = new Observable<number>(async (notify, fail, finish) => {
14 events = new Observable<number>(async (notify, fail, finish) => {
15 for (let i = 0; i < 10; i++) {
15 for (let i = 0; i < 10; i++) {
16 await delay(0);
16 await delay(0);
17 notify(i);
17 notify(i);
18 }
18 }
19 finish();
19 finish();
20 resolve();
20 resolve();
21 });
21 });
22 });
22 });
23
23
24 let count = 0;
24 let count = 0;
25 let complete = false;
25 let complete = false;
26 events.on(x => count = count + x, null, () => complete = true);
26 events.on(x => count = count + x, null, () => complete = true);
27
27
28 const first = await events.next();
28 const first = await events.next();
29
29
30 t.equals(first, 0, "the first event");
30 t.equals(first, 0, "the first event");
31 t.false(complete, "the sequence is not complete");
31 t.false(complete, "the sequence is not complete");
32
32
33 await done;
33 await done;
34
34
35 t.equals(count, 45, "the summ of the evetns");
35 t.equals(count, 45, "the summ of the evetns");
36 t.true(complete, "the sequence is complete");
36 t.true(complete, "the sequence is complete");
37
37
38 t.end();
38 t.end();
39 });
39 });
40
40
41 tape("event sequence termination", async t => {
41 tape("event sequence termination", async t => {
42 let events: IObservable<number>;
42 let events: IObservable<number>;
43
43
44 const done = new Promise<void>(resolve => {
44 const done = new Promise<void>(resolve => {
45 events = new Observable<number>(async (notify, fail, complete) => {
45 events = new Observable<number>(async (notify, fail, complete) => {
46 await delay(0);
46 await delay(0);
47 notify(1);
47 notify(1);
48 complete();
48 complete();
49 notify(2);
49 notify(2);
50 complete();
50 complete();
51 fail("Sequence terminated");
51 fail("Sequence terminated");
52 resolve();
52 resolve();
53 });
53 });
54 });
54 });
55
55
56 let count = 0;
56 let count = 0;
57 events.on(() => {}, e => count++, () => count++);
57 events.on(() => {}, e => count++, () => count++);
58
58
59 const first = await events.next();
59 const first = await events.next();
60 t.equals(first, 1, "the first message");
60 t.equals(first, 1, "the first message");
61 try {
61 try {
62 await events.next();
62 await events.next();
63 t.fail("shoud throw an exception");
63 t.fail("shoud throw an exception");
64 } catch (e) {
64 } catch (e) {
65 t.pass("the sequence is terminated");
65 t.pass("the sequence is terminated");
66 }
66 }
67
67
68 await done;
68 await done;
69
69
70 t.equals(count, 1, "the sequence must be terminated once");
70 t.equals(count, 1, "the sequence must be terminated once");
71
71
72 t.end();
72 t.end();
73 });
73 });
@@ -1,86 +1,99
1 import tape = require("tape");
1 import tape = require("tape");
2 import { delay } from "./TestTraits";
3 import { Cancellation } from "@implab/core/Cancellation";
2 import { Cancellation } from "@implab/core/Cancellation";
4 import { first, isPromise } from "@implab/core/safe";
3 import { first, isPromise, firstWhere, delay, nowait } from "@implab/core/safe";
5
4
6 tape("await delay test", async t => {
5 tape("await delay test", async t => {
7 // schedule delay
6 // schedule delay
8 let resolved = false;
7 let resolved = false;
9 let res = delay(0).then(() => resolved = true);
8 let res = delay(0).then(() => resolved = true);
10
9
11 t.false(resolved, "the delay should be async");
10 t.false(resolved, "the delay should be async");
12
11
13 await res;
12 await res;
14 t.pass("await delay");
13 t.pass("await delay");
15
14
16 // create cancellation token
15 // create cancellation token
17 let cancel: (e?: any) => void;
16 let cancel: (e?: any) => void;
18 const ct = new Cancellation(c => cancel = c);
17 const ct = new Cancellation(c => cancel = c);
19
18
20 // schedule delay
19 // schedule delay
21 resolved = false;
20 resolved = false;
22 res = delay(0, ct).then(() => resolved = true);
21 res = delay(0, ct).then(() => resolved = true);
23
22
24 t.false(resolved, "created delay with ct");
23 t.false(resolved, "created delay with ct");
25
24
26 // cancel
25 // cancel
27 cancel();
26 cancel();
28
27
29 try {
28 try {
30 await res;
29 await res;
31 t.fail("the delay should fail when it is cancelled");
30 t.fail("the delay should fail when it is cancelled");
32 } catch {
31 } catch {
33 t.pass("the delay is cancelled");
32 t.pass("the delay is cancelled");
34 }
33 }
35
34
36 let died = false;
35 t.throws(() => {
37
36 // try schedule delay after the cancellation is requested
38 // try schedule delay after the cancellation is requested
37 nowait(delay(0, ct));
39 res = delay(0, ct).then(x => true, () => died = true);
38 }, "Should throw if cancelled before start");
40
41 t.false(died, "The delay should be scheduled even if the cancellation is requested");
42
43 await res;
44 t.true(died, "the delay should fail when cancelled");
45
39
46 t.end();
40 t.end();
47 });
41 });
48
42
49 tape("sequemce test", async t => {
43 tape("sequemce test", async t => {
50 const sequence = ["a", "b", "c"];
44 const sequence = ["a", "b", "c"];
51 const empty = [];
45 const empty = [];
52
46
53 // synchronous tests
47 // synchronous tests
54 t.equals(first(sequence), "a", "Should return the first element");
48 t.equals(first(sequence), "a", "Should return the first element");
49 t.equals(firstWhere(sequence, x => x === "b"), "b", "Should get the second element");
55
50
56 let v: string;
51 let v: string;
57 let e: Error;
52 let e: Error;
58 first(sequence, x => v = x);
53 first(sequence, x => v = x);
59 t.equal(v, "a", "The callback should be called for the first element");
54 t.equal(v, "a", "The callback should be called for the first element");
55 firstWhere(sequence, x => x === "b", x => v = x);
56 t.equal(v, "b", "The callback should be called for the second element");
60
57
61 t.throws(() => {
58 t.throws(() => {
62 first(empty);
59 first(empty);
63 }, "Should throw when the sequence is empty");
60 }, "Should throw when the sequence is empty");
64
61
65 t.throws(() => {
62 t.throws(() => {
63 firstWhere(empty, x => x === "b");
64 }, "Should throw when the sequence is empty");
65
66 t.throws(() => {
66 first(empty, x => v = x);
67 first(empty, x => v = x);
67 }, "Should throw when the sequence is empty");
68 }, "Should throw when the sequence is empty");
68
69
70 t.throws(() => {
71 firstWhere(empty, x => x === "b", x => v = x);
72 }, "Should throw when the sequence is empty");
73
74 t.throws(() => {
75 firstWhere(sequence, x => x === "z");
76 }, "Should throw when the element isn't found");
77
78 t.throws(() => {
79 firstWhere(sequence, x => x === "z", x => v = x);
80 }, "Should throw when the element isn't found");
81
69 first(empty, null, x => e = x);
82 first(empty, null, x => e = x);
70 t.true(e, "The errorback should be called for the empty sequence");
83 t.true(e, "The errorback should be called for the empty sequence");
71
84
72 // async tests
85 // async tests
73 const asyncSequence = Promise.resolve(sequence);
86 const asyncSequence = Promise.resolve(sequence);
74 const asyncEmptySequence = Promise.resolve(empty);
87 const asyncEmptySequence = Promise.resolve(empty);
75
88
76 const promise = first(asyncSequence);
89 const promise = first(asyncSequence);
77 t.true(isPromise(promise), "Should return promise");
90 t.true(isPromise(promise), "Should return promise");
78
91
79 v = await promise;
92 v = await promise;
80 t.equal(v, "a", "Should return the first element");
93 t.equal(v, "a", "Should return the first element");
81
94
82 v = await new Promise(resolve => first(asyncSequence, resolve));
95 v = await new Promise(resolve => first(asyncSequence, resolve));
83 t.equal(v, "a", "The callback should be called for the first element");
96 t.equal(v, "a", "The callback should be called for the first element");
84
97
85 t.end();
98 t.end();
86 });
99 });
@@ -1,88 +1,65
1 import { IObservable, ICancellation, IDestroyable } from "@implab/core/interfaces";
1 import { IObservable, ICancellation, IDestroyable } from "@implab/core/interfaces";
2 import { Cancellation } from "@implab/core/Cancellation";
2 import { Cancellation } from "@implab/core/Cancellation";
3 import { TraceEvent, LogLevel, WarnLevel, DebugLevel, TraceSource } from "@implab/core/log/TraceSource";
3 import { TraceEvent, LogLevel, WarnLevel, DebugLevel, TraceSource } from "@implab/core/log/TraceSource";
4 import * as tape from "tape";
4 import * as tape from "tape";
5 import { argumentNotNull, destroy } from "@implab/core/safe";
5 import { argumentNotNull, destroy } from "@implab/core/safe";
6
6
7 export class TapeWriter implements IDestroyable {
7 export class TapeWriter implements IDestroyable {
8 readonly _tape: tape.Test;
8 readonly _tape: tape.Test;
9
9
10 _subscriptions = new Array<IDestroyable>();
10 _subscriptions = new Array<IDestroyable>();
11
11
12 constructor(t: tape.Test) {
12 constructor(t: tape.Test) {
13 argumentNotNull(t, "tape");
13 argumentNotNull(t, "tape");
14 this._tape = t;
14 this._tape = t;
15 }
15 }
16
16
17 writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
17 writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
18 const subscription = source.on(this.writeEvent.bind(this));
18 const subscription = source.on(this.writeEvent.bind(this));
19 if (ct.isSupported()) {
19 if (ct.isSupported()) {
20 ct.register(subscription.destroy.bind(subscription));
20 ct.register(subscription.destroy.bind(subscription));
21 }
21 }
22 this._subscriptions.push(subscription);
22 this._subscriptions.push(subscription);
23 }
23 }
24
24
25 writeEvent(next: TraceEvent) {
25 writeEvent(next: TraceEvent) {
26 if (next.level >= DebugLevel) {
26 if (next.level >= DebugLevel) {
27 this._tape.comment(`DEBUG ${next.source.id} ${next.arg}`);
27 this._tape.comment(`DEBUG ${next.source.id} ${next.arg}`);
28 } else if (next.level >= LogLevel) {
28 } else if (next.level >= LogLevel) {
29 this._tape.comment(`LOG ${next.source.id} ${next.arg}`);
29 this._tape.comment(`LOG ${next.source.id} ${next.arg}`);
30 } else if (next.level >= WarnLevel) {
30 } else if (next.level >= WarnLevel) {
31 this._tape.comment(`WARN ${next.source.id} ${next.arg}`);
31 this._tape.comment(`WARN ${next.source.id} ${next.arg}`);
32 } else {
32 } else {
33 this._tape.comment(`ERROR ${next.source.id} ${next.arg}`);
33 this._tape.comment(`ERROR ${next.source.id} ${next.arg}`);
34 }
34 }
35 }
35 }
36
36
37 destroy() {
37 destroy() {
38 this._subscriptions.forEach(destroy);
38 this._subscriptions.forEach(destroy);
39 }
39 }
40 }
40 }
41
41
42 export async function delay(timeout: number, ct: ICancellation = Cancellation.none) {
43 let un: IDestroyable;
44
45 try {
46 await new Promise((resolve, reject) => {
47 if (ct.isRequested()) {
48 un = ct.register(reject);
49 } else {
50 const ht = setTimeout(() => {
51 resolve();
52 }, timeout);
53
54 un = ct.register(e => {
55 clearTimeout(ht);
56 reject(e);
57 });
58 }
59 });
60 } finally {
61 destroy(un);
62 }
63 }
64
65 export function test(name: string, cb: (t: tape.Test) => any) {
42 export function test(name: string, cb: (t: tape.Test) => any) {
66 tape(name, async t => {
43 tape(name, async t => {
67 const writer = new TapeWriter(t);
44 const writer = new TapeWriter(t);
68
45
69 TraceSource.on(ts => {
46 TraceSource.on(ts => {
70 ts.level = DebugLevel;
47 ts.level = DebugLevel;
71 writer.writeEvents(ts.events);
48 writer.writeEvents(ts.events);
72 });
49 });
73
50
74 try {
51 try {
75 await cb(t);
52 await cb(t);
76 } catch (e) {
53 } catch (e) {
77
54
78 // verbose error information
55 // verbose error information
79 // tslint:disable-next-line
56 // tslint:disable-next-line
80 console.error(e);
57 console.error(e);
81 t.fail(e);
58 t.fail(e);
82
59
83 } finally {
60 } finally {
84 t.end();
61 t.end();
85 destroy(writer);
62 destroy(writer);
86 }
63 }
87 });
64 });
88 }
65 }
General Comments 0
You need to be logged in to leave comments. Login now