UNPKG

19.1 kBJavaScriptView Raw
1// Copyright (c) Microsoft, All rights reserved. See License.txt in the project root for license information.
2
3;(function (factory) {
4 var objectTypes = {
5 'function': true,
6 'object': true
7 };
8
9 function checkGlobal(value) {
10 return (value && value.Object === Object) ? value : null;
11 }
12
13 var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType) ? exports : null;
14 var freeModule = (objectTypes[typeof module] && module && !module.nodeType) ? module : null;
15 var freeGlobal = checkGlobal(freeExports && freeModule && typeof global === 'object' && global);
16 var freeSelf = checkGlobal(objectTypes[typeof self] && self);
17 var freeWindow = checkGlobal(objectTypes[typeof window] && window);
18 var moduleExports = (freeModule && freeModule.exports === freeExports) ? freeExports : null;
19 var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
20 var root = freeGlobal || ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) || freeSelf || thisGlobal || Function('return this')();
21
22 // Because of build optimizers
23 if (typeof define === 'function' && define.amd) {
24 define(['./rx.virtualtime', 'exports'], function (Rx, exports) {
25 root.Rx = factory(root, exports, Rx);
26 return root.Rx;
27 });
28 } else if (typeof module === 'object' && module && module.exports === freeExports) {
29 module.exports = factory(root, module.exports, require('./rx'));
30 } else {
31 root.Rx = factory(root, {}, root.Rx);
32 }
33}.call(this, function (root, exp, Rx, undefined) {
34
35 // Defaults
36 var Observer = Rx.Observer,
37 Observable = Rx.Observable,
38 Notification = Rx.Notification,
39 VirtualTimeScheduler = Rx.VirtualTimeScheduler,
40 Disposable = Rx.Disposable,
41 disposableEmpty = Disposable.empty,
42 disposableCreate = Disposable.create,
43 CompositeDisposable = Rx.CompositeDisposable,
44 inherits = Rx.internals.inherits,
45 defaultComparer = Rx.internals.isEqual;
46
47function OnNextPredicate(predicate) {
48 this.predicate = predicate;
49}
50
51OnNextPredicate.prototype.equals = function (other) {
52 if (other === this) { return true; }
53 if (other == null) { return false; }
54 if (other.kind !== 'N') { return false; }
55 return this.predicate(other.value);
56};
57
58function OnErrorPredicate(predicate) {
59 this.predicate = predicate;
60}
61
62OnErrorPredicate.prototype.equals = function (other) {
63 if (other === this) { return true; }
64 if (other == null) { return false; }
65 if (other.kind !== 'E') { return false; }
66 return this.predicate(other.error);
67};
68
69var ReactiveTest = Rx.ReactiveTest = {
70 /** Default virtual time used for creation of observable sequences in unit tests. */
71 created: 100,
72 /** Default virtual time used to subscribe to observable sequences in unit tests. */
73 subscribed: 200,
74 /** Default virtual time used to dispose subscriptions in unit tests. */
75 disposed: 1000,
76
77 /**
78 * Factory method for an OnNext notification record at a given time with a given value or a predicate function.
79 *
80 * 1 - ReactiveTest.onNext(200, 42);
81 * 2 - ReactiveTest.onNext(200, function (x) { return x.length == 2; });
82 *
83 * @param ticks Recorded virtual time the OnNext notification occurs.
84 * @param value Recorded value stored in the OnNext notification or a predicate.
85 * @return Recorded OnNext notification.
86 */
87 onNext: function (ticks, value) {
88 return typeof value === 'function' ?
89 new Recorded(ticks, new OnNextPredicate(value)) :
90 new Recorded(ticks, Notification.createOnNext(value));
91 },
92 /**
93 * Factory method for an OnError notification record at a given time with a given error.
94 *
95 * 1 - ReactiveTest.onNext(200, new Error('error'));
96 * 2 - ReactiveTest.onNext(200, function (e) { return e.message === 'error'; });
97 *
98 * @param ticks Recorded virtual time the OnError notification occurs.
99 * @param exception Recorded exception stored in the OnError notification.
100 * @return Recorded OnError notification.
101 */
102 onError: function (ticks, error) {
103 return typeof error === 'function' ?
104 new Recorded(ticks, new OnErrorPredicate(error)) :
105 new Recorded(ticks, Notification.createOnError(error));
106 },
107 /**
108 * Factory method for an OnCompleted notification record at a given time.
109 *
110 * @param ticks Recorded virtual time the OnCompleted notification occurs.
111 * @return Recorded OnCompleted notification.
112 */
113 onCompleted: function (ticks) {
114 return new Recorded(ticks, Notification.createOnCompleted());
115 },
116 /**
117 * Factory method for a subscription record based on a given subscription and disposal time.
118 *
119 * @param start Virtual time indicating when the subscription was created.
120 * @param end Virtual time indicating when the subscription was disposed.
121 * @return Subscription object.
122 */
123 subscribe: function (start, end) {
124 return new Subscription(start, end);
125 }
126};
127
128 /**
129 * Creates a new object recording the production of the specified value at the given virtual time.
130 *
131 * @constructor
132 * @param {Number} time Virtual time the value was produced on.
133 * @param {Mixed} value Value that was produced.
134 * @param {Function} comparer An optional comparer.
135 */
136 var Recorded = Rx.Recorded = function (time, value, comparer) {
137 this.time = time;
138 this.value = value;
139 this.comparer = comparer || defaultComparer;
140 };
141
142 /**
143 * Checks whether the given recorded object is equal to the current instance.
144 *
145 * @param {Recorded} other Recorded object to check for equality.
146 * @returns {Boolean} true if both objects are equal; false otherwise.
147 */
148 Recorded.prototype.equals = function (other) {
149 return this.time === other.time && this.comparer(this.value, other.value);
150 };
151
152 /**
153 * Returns a string representation of the current Recorded value.
154 *
155 * @returns {String} String representation of the current Recorded value.
156 */
157 Recorded.prototype.toString = function () {
158 return this.value.toString() + '@' + this.time;
159 };
160
161 /**
162 * Creates a new subscription object with the given virtual subscription and unsubscription time.
163 *
164 * @constructor
165 * @param {Number} subscribe Virtual time at which the subscription occurred.
166 * @param {Number} unsubscribe Virtual time at which the unsubscription occurred.
167 */
168 var Subscription = Rx.Subscription = function (start, end) {
169 this.subscribe = start;
170 this.unsubscribe = end || Number.MAX_VALUE;
171 };
172
173 /**
174 * Checks whether the given subscription is equal to the current instance.
175 * @param other Subscription object to check for equality.
176 * @returns {Boolean} true if both objects are equal; false otherwise.
177 */
178 Subscription.prototype.equals = function (other) {
179 return this.subscribe === other.subscribe && this.unsubscribe === other.unsubscribe;
180 };
181
182 /**
183 * Returns a string representation of the current Subscription value.
184 * @returns {String} String representation of the current Subscription value.
185 */
186 Subscription.prototype.toString = function () {
187 return '(' + this.subscribe + ', ' + (this.unsubscribe === Number.MAX_VALUE ? 'Infinite' : this.unsubscribe) + ')';
188 };
189
190 var MockDisposable = Rx.MockDisposable = function (scheduler) {
191 this.scheduler = scheduler;
192 this.disposes = [];
193 this.disposes.push(this.scheduler.clock);
194 };
195
196 MockDisposable.prototype.dispose = function () {
197 this.disposes.push(this.scheduler.clock);
198 };
199
200 var MockObserver = (function (__super__) {
201 inherits(MockObserver, __super__);
202
203 function MockObserver(scheduler) {
204 __super__.call(this);
205 this.scheduler = scheduler;
206 this.messages = [];
207 }
208
209 var MockObserverPrototype = MockObserver.prototype;
210
211 MockObserverPrototype.onNext = function (value) {
212 this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnNext(value)));
213 };
214
215 MockObserverPrototype.onError = function (e) {
216 this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnError(e)));
217 };
218
219 MockObserverPrototype.onCompleted = function () {
220 this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnCompleted()));
221 };
222
223 return MockObserver;
224 })(Observer);
225
226 function MockPromise(scheduler, messages) {
227 var self = this;
228 this.scheduler = scheduler;
229 this.messages = messages;
230 this.subscriptions = [];
231 this.observers = [];
232 for (var i = 0, len = this.messages.length; i < len; i++) {
233 var message = this.messages[i],
234 notification = message.value;
235 (function (innerNotification) {
236 scheduler.scheduleAbsolute(null, message.time, function () {
237 var obs = self.observers.slice(0);
238
239 for (var j = 0, jLen = obs.length; j < jLen; j++) {
240 innerNotification.accept(obs[j]);
241 }
242 return disposableEmpty;
243 });
244 })(notification);
245 }
246 }
247
248 MockPromise.prototype.then = function (onResolved, onRejected) {
249 var self = this;
250
251 this.subscriptions.push(new Subscription(this.scheduler.clock));
252 var index = this.subscriptions.length - 1;
253
254 var newPromise;
255
256 var observer = Rx.Observer.create(
257 function (x) {
258 var retValue = onResolved(x);
259 if (retValue && typeof retValue.then === 'function') {
260 newPromise = retValue;
261 } else {
262 var ticks = self.scheduler.clock;
263 newPromise = new MockPromise(self.scheduler, [Rx.ReactiveTest.onNext(ticks, undefined), Rx.ReactiveTest.onCompleted(ticks)]);
264 }
265 var idx = self.observers.indexOf(observer);
266 self.observers.splice(idx, 1);
267 self.subscriptions[index] = new Subscription(self.subscriptions[index].subscribe, self.scheduler.clock);
268 },
269 function (err) {
270 onRejected(err);
271 var idx = self.observers.indexOf(observer);
272 self.observers.splice(idx, 1);
273 self.subscriptions[index] = new Subscription(self.subscriptions[index].subscribe, self.scheduler.clock);
274 }
275 );
276 this.observers.push(observer);
277
278 return newPromise || new MockPromise(this.scheduler, this.messages);
279 };
280
281 var HotObservable = (function (__super__) {
282 inherits(HotObservable, __super__);
283
284 function HotObservable(scheduler, messages) {
285 __super__.call(this);
286 var message, notification, observable = this;
287 this.scheduler = scheduler;
288 this.messages = messages;
289 this.subscriptions = [];
290 this.observers = [];
291 for (var i = 0, len = this.messages.length; i < len; i++) {
292 message = this.messages[i];
293 notification = message.value;
294 (function (innerNotification) {
295 scheduler.scheduleAbsolute(null, message.time, function () {
296 var obs = observable.observers.slice(0);
297
298 for (var j = 0, jLen = obs.length; j < jLen; j++) {
299 innerNotification.accept(obs[j]);
300 }
301 return disposableEmpty;
302 });
303 })(notification);
304 }
305 }
306
307 HotObservable.prototype._subscribe = function (o) {
308 var observable = this;
309 this.observers.push(o);
310 this.subscriptions.push(new Subscription(this.scheduler.clock));
311 var index = this.subscriptions.length - 1;
312 return disposableCreate(function () {
313 var idx = observable.observers.indexOf(o);
314 observable.observers.splice(idx, 1);
315 observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
316 });
317 };
318
319 return HotObservable;
320 })(Observable);
321
322 var ColdObservable = (function (__super__) {
323 inherits(ColdObservable, __super__);
324
325 function ColdObservable(scheduler, messages) {
326 __super__.call(this);
327 this.scheduler = scheduler;
328 this.messages = messages;
329 this.subscriptions = [];
330 }
331
332 ColdObservable.prototype._subscribe = function (o) {
333 var message, notification, observable = this;
334 this.subscriptions.push(new Subscription(this.scheduler.clock));
335 var index = this.subscriptions.length - 1;
336 var d = new CompositeDisposable();
337 for (var i = 0, len = this.messages.length; i < len; i++) {
338 message = this.messages[i];
339 notification = message.value;
340 (function (innerNotification) {
341 d.add(observable.scheduler.scheduleRelative(null, message.time, function () {
342 innerNotification.accept(o);
343 return disposableEmpty;
344 }));
345 })(notification);
346 }
347 return disposableCreate(function () {
348 observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
349 d.dispose();
350 });
351 };
352
353 return ColdObservable;
354 })(Observable);
355
356 /** Virtual time scheduler used for testing applications and libraries built using Reactive Extensions. */
357 Rx.TestScheduler = (function (__super__) {
358 inherits(TestScheduler, __super__);
359
360 function baseComparer(x, y) {
361 return x > y ? 1 : (x < y ? -1 : 0);
362 }
363
364 function TestScheduler() {
365 __super__.call(this, 0, baseComparer);
366 }
367
368 /**
369 * Schedules an action to be executed at the specified virtual time.
370 *
371 * @param state State passed to the action to be executed.
372 * @param dueTime Absolute virtual time at which to execute the action.
373 * @param action Action to be executed.
374 * @return Disposable object used to cancel the scheduled action (best effort).
375 */
376 TestScheduler.prototype.scheduleAbsolute = function (state, dueTime, action) {
377 dueTime <= this.clock && (dueTime = this.clock + 1);
378 return __super__.prototype.scheduleAbsolute.call(this, state, dueTime, action);
379 };
380 /**
381 * Adds a relative virtual time to an absolute virtual time value.
382 *
383 * @param absolute Absolute virtual time value.
384 * @param relative Relative virtual time value to add.
385 * @return Resulting absolute virtual time sum value.
386 */
387 TestScheduler.prototype.add = function (absolute, relative) {
388 return absolute + relative;
389 };
390 /**
391 * Converts the absolute virtual time value to a DateTimeOffset value.
392 *
393 * @param absolute Absolute virtual time value to convert.
394 * @return Corresponding DateTimeOffset value.
395 */
396 TestScheduler.prototype.toAbsoluteTime = function (absolute) {
397 return new Date(absolute).getTime();
398 };
399 /**
400 * Converts the TimeSpan value to a relative virtual time value.
401 *
402 * @param timeSpan TimeSpan value to convert.
403 * @return Corresponding relative virtual time value.
404 */
405 TestScheduler.prototype.toRelativeTime = function (timeSpan) {
406 return timeSpan;
407 };
408 /**
409 * Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the resulting sequence, and dispose the subscription.
410 *
411 * @param create Factory method to create an observable sequence.
412 * @param created Virtual time at which to invoke the factory to create an observable sequence.
413 * @param subscribed Virtual time at which to subscribe to the created observable sequence.
414 * @param disposed Virtual time at which to dispose the subscription.
415 * @return Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
416 */
417 TestScheduler.prototype.startScheduler = function (createFn, settings) {
418 settings || (settings = {});
419 settings.created == null && (settings.created = ReactiveTest.created);
420 settings.subscribed == null && (settings.subscribed = ReactiveTest.subscribed);
421 settings.disposed == null && (settings.disposed = ReactiveTest.disposed);
422
423 var observer = this.createObserver(), source, subscription;
424
425 this.scheduleAbsolute(null, settings.created, function () {
426 source = createFn();
427 return disposableEmpty;
428 });
429
430 this.scheduleAbsolute(null, settings.subscribed, function () {
431 subscription = source.subscribe(observer);
432 return disposableEmpty;
433 });
434
435 this.scheduleAbsolute(null, settings.disposed, function () {
436 subscription.dispose();
437 return disposableEmpty;
438 });
439
440 this.start();
441
442 return observer;
443 };
444
445 /**
446 * Creates a hot observable using the specified timestamped notification messages either as an array or arguments.
447 * @param messages Notifications to surface through the created sequence at their specified absolute virtual times.
448 * @return Hot observable sequence that can be used to assert the timing of subscriptions and notifications.
449 */
450 TestScheduler.prototype.createHotObservable = function () {
451 var len = arguments.length, args;
452 if (Array.isArray(arguments[0])) {
453 args = arguments[0];
454 } else {
455 args = new Array(len);
456 for (var i = 0; i < len; i++) { args[i] = arguments[i]; }
457 }
458 return new HotObservable(this, args);
459 };
460
461 /**
462 * Creates a cold observable using the specified timestamped notification messages either as an array or arguments.
463 * @param messages Notifications to surface through the created sequence at their specified virtual time offsets from the sequence subscription time.
464 * @return Cold observable sequence that can be used to assert the timing of subscriptions and notifications.
465 */
466 TestScheduler.prototype.createColdObservable = function () {
467 var len = arguments.length, args;
468 if (Array.isArray(arguments[0])) {
469 args = arguments[0];
470 } else {
471 args = new Array(len);
472 for (var i = 0; i < len; i++) { args[i] = arguments[i]; }
473 }
474 return new ColdObservable(this, args);
475 };
476
477 /**
478 * Creates a resolved promise with the given value and ticks
479 * @param {Number} ticks The absolute time of the resolution.
480 * @param {Any} value The value to yield at the given tick.
481 * @returns {MockPromise} A mock Promise which fulfills with the given value.
482 */
483 TestScheduler.prototype.createResolvedPromise = function (ticks, value) {
484 return new MockPromise(this, [Rx.ReactiveTest.onNext(ticks, value), Rx.ReactiveTest.onCompleted(ticks)]);
485 };
486
487 /**
488 * Creates a rejected promise with the given reason and ticks
489 * @param {Number} ticks The absolute time of the resolution.
490 * @param {Any} reason The reason for rejection to yield at the given tick.
491 * @returns {MockPromise} A mock Promise which rejects with the given reason.
492 */
493 TestScheduler.prototype.createRejectedPromise = function (ticks, reason) {
494 return new MockPromise(this, [Rx.ReactiveTest.onError(ticks, reason)]);
495 };
496
497 /**
498 * Creates an observer that records received notification messages and timestamps those.
499 * @return Observer that can be used to assert the timing of received notifications.
500 */
501 TestScheduler.prototype.createObserver = function () {
502 return new MockObserver(this);
503 };
504
505 return TestScheduler;
506 })(VirtualTimeScheduler);
507
508 return Rx;
509}));