From cbc58c199884cedffe945f33c0cbc01b9131a3aa Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Tue, 8 Aug 2017 13:13:38 +0900 Subject: [PATCH] fix(rxjs): fix #863, fix asap scheduler issue, add testcases (#848) * fix(rxjs): fix asap scheduler issue, add testcases * patch defer, add cases for concat/defer * add testcases: from/forkJoin * add cases: fromEvent/fromPromise/interval * add merge/never/of/range/throw/timer/zip * add audit~delay * staging changes * fix test cases * add notification test * add merge/map/collections * add remained cases * patch multicast * use _subscriber setter instead of patching Observable constructor * refactor * update gulpfiles * check proto null * add null check if operators are not loaded * remove not necessary karma settings --- gulpfile.js | 9 +- karma-base.conf.js | 5 +- karma-dist.conf.js | 2 - lib/rxjs/rxjs.ts | 396 ++++++---- test/browser-zone-setup.ts | 3 +- test/global-rxjs.ts | 11 - test/main.ts | 12 +- test/rxjs/rxjs.Observable.audit.spec.ts | 83 ++ test/rxjs/rxjs.Observable.buffer.spec.ts | 182 +++++ test/rxjs/rxjs.Observable.catch.spec.ts | 86 ++ test/rxjs/rxjs.Observable.collection.spec.ts | 735 ++++++++++++++++++ test/rxjs/rxjs.Observable.combine.spec.ts | 148 ++++ test/rxjs/rxjs.Observable.concat.spec.ts | 203 +++++ test/rxjs/rxjs.Observable.count.spec.ts | 44 ++ test/rxjs/rxjs.Observable.debounce.spec.ts | 71 ++ test/rxjs/rxjs.Observable.default.spec.ts | 45 ++ test/rxjs/rxjs.Observable.delay.spec.ts | 70 ++ test/rxjs/rxjs.Observable.distinct.spec.ts | 96 +++ test/rxjs/rxjs.Observable.do.spec.ts | 50 ++ test/rxjs/rxjs.Observable.map.spec.ts | 109 +++ test/rxjs/rxjs.Observable.merge.spec.ts | 240 ++++++ test/rxjs/rxjs.Observable.multicast.spec.ts | 78 ++ .../rxjs/rxjs.Observable.notification.spec.ts | 54 ++ test/rxjs/rxjs.Observable.race.spec.ts | 45 ++ test/rxjs/rxjs.Observable.sample.spec.ts | 72 ++ test/rxjs/rxjs.Observable.take.spec.ts | 124 +++ test/rxjs/rxjs.Observable.timeout.spec.ts | 63 ++ test/rxjs/rxjs.Observable.window.spec.ts | 148 ++++ test/rxjs/rxjs.bindCallback.spec.ts | 37 +- test/rxjs/rxjs.bindNodeCallback.spec.ts | 37 +- test/rxjs/rxjs.combineLatest.spec.ts | 36 +- test/rxjs/rxjs.common.spec.ts | 1 + test/rxjs/rxjs.concat.spec.ts | 96 +++ test/rxjs/rxjs.defer.spec.ts | 46 ++ test/rxjs/rxjs.empty.spec.ts | 38 + test/rxjs/rxjs.forkjoin.spec.ts | 70 ++ test/rxjs/rxjs.from.spec.ts | 95 +++ test/rxjs/rxjs.fromEvent.spec.ts | 106 +++ test/rxjs/rxjs.fromPromise.spec.ts | 51 ++ test/rxjs/rxjs.interval.spec.ts | 43 + test/rxjs/rxjs.merge.spec.ts | 52 ++ test/rxjs/rxjs.never.spec.ts | 41 + test/rxjs/rxjs.of.spec.ts | 42 + test/rxjs/rxjs.range.spec.ts | 70 ++ test/rxjs/rxjs.spec.ts | 40 +- test/rxjs/rxjs.throw.spec.ts | 70 ++ test/rxjs/rxjs.timer.spec.ts | 47 ++ test/rxjs/rxjs.zip.spec.ts | 51 ++ test/test-util.ts | 2 +- 49 files changed, 4057 insertions(+), 198 deletions(-) delete mode 100644 test/global-rxjs.ts create mode 100644 test/rxjs/rxjs.Observable.audit.spec.ts create mode 100644 test/rxjs/rxjs.Observable.buffer.spec.ts create mode 100644 test/rxjs/rxjs.Observable.catch.spec.ts create mode 100644 test/rxjs/rxjs.Observable.collection.spec.ts create mode 100644 test/rxjs/rxjs.Observable.combine.spec.ts create mode 100644 test/rxjs/rxjs.Observable.concat.spec.ts create mode 100644 test/rxjs/rxjs.Observable.count.spec.ts create mode 100644 test/rxjs/rxjs.Observable.debounce.spec.ts create mode 100644 test/rxjs/rxjs.Observable.default.spec.ts create mode 100644 test/rxjs/rxjs.Observable.delay.spec.ts create mode 100644 test/rxjs/rxjs.Observable.distinct.spec.ts create mode 100644 test/rxjs/rxjs.Observable.do.spec.ts create mode 100644 test/rxjs/rxjs.Observable.map.spec.ts create mode 100644 test/rxjs/rxjs.Observable.merge.spec.ts create mode 100644 test/rxjs/rxjs.Observable.multicast.spec.ts create mode 100644 test/rxjs/rxjs.Observable.notification.spec.ts create mode 100644 test/rxjs/rxjs.Observable.race.spec.ts create mode 100644 test/rxjs/rxjs.Observable.sample.spec.ts create mode 100644 test/rxjs/rxjs.Observable.take.spec.ts create mode 100644 test/rxjs/rxjs.Observable.timeout.spec.ts create mode 100644 test/rxjs/rxjs.Observable.window.spec.ts create mode 100644 test/rxjs/rxjs.concat.spec.ts create mode 100644 test/rxjs/rxjs.defer.spec.ts create mode 100644 test/rxjs/rxjs.empty.spec.ts create mode 100644 test/rxjs/rxjs.forkjoin.spec.ts create mode 100644 test/rxjs/rxjs.from.spec.ts create mode 100644 test/rxjs/rxjs.fromEvent.spec.ts create mode 100644 test/rxjs/rxjs.fromPromise.spec.ts create mode 100644 test/rxjs/rxjs.interval.spec.ts create mode 100644 test/rxjs/rxjs.merge.spec.ts create mode 100644 test/rxjs/rxjs.never.spec.ts create mode 100644 test/rxjs/rxjs.of.spec.ts create mode 100644 test/rxjs/rxjs.range.spec.ts create mode 100644 test/rxjs/rxjs.throw.spec.ts create mode 100644 test/rxjs/rxjs.timer.spec.ts create mode 100644 test/rxjs/rxjs.zip.spec.ts diff --git a/gulpfile.js b/gulpfile.js index 9ae07cfe7..ccd5a2327 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -29,7 +29,14 @@ function generateScript(inFile, outFile, minify, callback) { + '*\n' + '* Use of this source code is governed by an MIT-style license that can be\n' + '* found in the LICENSE file at https://angular.io/license\n' - + '*/' + + '*/', + globals: { + 'rxjs/Observable': 'Rx', + 'rxjs/Subscriber': 'Rx', + 'rxjs/Subscription': 'Rx', + 'rxjs/scheduler/asap': 'Rx.Scheduler', + 'rxjs/symbol/rxSubscriber': 'Rx.Symbol' + } })) .pipe(rename(outFile)), ]; diff --git a/karma-base.conf.js b/karma-base.conf.js index c4fb05a91..b733dbe4f 100644 --- a/karma-base.conf.js +++ b/karma-base.conf.js @@ -13,7 +13,10 @@ module.exports = function (config) { 'node_modules/systemjs/dist/system-polyfills.js', 'node_modules/systemjs/dist/system.src.js', 'node_modules/whatwg-fetch/fetch.js', - {pattern: 'node_modules/rxjs/bundles/Rx.js', watched: true, served: true, included: false}, + {pattern: 'node_modules/rxjs/**/**/*.js', included: false, watched: false }, + {pattern: 'node_modules/rxjs/**/**/*.js.map', included: false, watched: false }, + {pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, + {pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false }, {pattern: 'test/assets/**/*.*', watched: true, served: true, included: false}, {pattern: 'build/**/*.js.map', watched: true, served: true, included: false}, {pattern: 'build/**/*.js', watched: true, served: true, included: false} diff --git a/karma-dist.conf.js b/karma-dist.conf.js index 0632f8d5d..844aef697 100644 --- a/karma-dist.conf.js +++ b/karma-dist.conf.js @@ -18,7 +18,5 @@ module.exports = function (config) { config.files.push('dist/sync-test.js'); config.files.push('dist/task-tracking.js'); config.files.push('dist/wtf.js'); - config.files.push('node_modules/rxjs/bundles/Rx.js'); - config.files.push('dist/zone-patch-rxjs.js'); config.files.push('build/test/main.js'); }; diff --git a/lib/rxjs/rxjs.ts b/lib/rxjs/rxjs.ts index 0732c1dba..1e14147b8 100644 --- a/lib/rxjs/rxjs.ts +++ b/lib/rxjs/rxjs.ts @@ -6,7 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ -import * as Rx from 'rxjs/Rx'; +import 'rxjs/add/observable/bindCallback'; +import 'rxjs/add/observable/bindNodeCallback'; +import 'rxjs/add/observable/defer'; +import 'rxjs/add/observable/forkJoin'; +import 'rxjs/add/observable/fromEventPattern'; +import 'rxjs/add/operator/multicast'; + +import {Observable} from 'rxjs/Observable'; +import {asap} from 'rxjs/scheduler/asap'; +import {Subscriber} from 'rxjs/Subscriber'; +import {Subscription} from 'rxjs/Subscription'; +import {rxSubscriber} from 'rxjs/symbol/rxSubscriber'; (Zone as any).__load_patch('rxjs', (global: any, Zone: ZoneType, api: any) => { const symbol: (symbolString: string) => string = (Zone as any).__symbol__; @@ -17,144 +28,139 @@ import * as Rx from 'rxjs/Rx'; const unsubscribeSource = 'rxjs.Subscriber.unsubscribe'; const teardownSource = 'rxjs.Subscriber.teardownLogic'; - const patchObservableInstance = function(observable: any) { - observable._zone = Zone.current; - // patch inner function this._subscribe to check - // SubscriptionZone is same with ConstuctorZone or not - if (observable._subscribe && typeof observable._subscribe === 'function' && - !observable._originalSubscribe) { - observable._originalSubscribe = observable._subscribe; - observable._subscribe = _patchedSubscribe; - } + const empty = { + closed: true, + next(value: any): void{}, + error(err: any): void{throw err;}, + complete(): void{} }; - const _patchedSubscribe = function() { - const currentZone = Zone.current; - const _zone = this._zone; + function toSubscriber( + nextOrObserver?: any, error?: (error: any) => void, complete?: () => void): Subscriber { + if (nextOrObserver) { + if (nextOrObserver instanceof Subscriber) { + return (>nextOrObserver); + } - const args = Array.prototype.slice.call(arguments); - const subscriber = args.length > 0 ? args[0] : undefined; - // also keep currentZone in Subscriber - // for later Subscriber.next/error/complete method - if (subscriber && !subscriber._zone) { - subscriber._zone = currentZone; - } - // _subscribe should run in ConstructorZone - // but for performance concern, we should check - // whether ConsturctorZone === Zone.current here - const tearDownLogic = _zone !== Zone.current ? - _zone.run(this._originalSubscribe, this, args, subscribeSource) : - this._originalSubscribe.apply(this, args); - if (tearDownLogic && typeof tearDownLogic === 'function') { - const patchedTearDownLogic = function() { - // tearDownLogic should also run in ConstructorZone - // but for performance concern, we should check - // whether ConsturctorZone === Zone.current here - if (_zone && _zone !== Zone.current) { - return _zone.run(tearDownLogic, this, arguments, teardownSource); - } else { - return tearDownLogic.apply(this, arguments); - } - }; - return patchedTearDownLogic; + if (nextOrObserver[rxSubscriber]) { + return nextOrObserver[rxSubscriber](); + } } - return tearDownLogic; - }; - const patchObservable = function(Rx: any, observableType: string) { - const symbolObservable = symbol(observableType); - - const Observable = Rx[observableType]; - if (!Observable || Observable[symbolObservable]) { - // the subclass of Observable not loaded or have been patched - return; + if (!nextOrObserver && !error && !complete) { + return new Subscriber(empty); } - // monkey-patch Observable to save the - // current zone as ConstructorZone - const patchedObservable: any = Rx[observableType] = function() { - Observable.apply(this, arguments); - patchObservableInstance(this); - return this; - }; - - patchedObservable.prototype = Observable.prototype; - patchedObservable[symbolObservable] = Observable; - - Object.keys(Observable).forEach(key => { - patchedObservable[key] = Observable[key]; - }); + return new Subscriber(nextOrObserver, error, complete); + } + const patchObservable = function() { const ObservablePrototype: any = Observable.prototype; const symbolSubscribe = symbol('subscribe'); + const _symbolSubscribe = symbol('_subscribe'); + const _subscribe = ObservablePrototype[_symbolSubscribe] = ObservablePrototype._subscribe; + const subscribe = ObservablePrototype[symbolSubscribe] = ObservablePrototype.subscribe; - if (!ObservablePrototype[symbolSubscribe]) { - const subscribe = ObservablePrototype[symbolSubscribe] = ObservablePrototype.subscribe; - // patch Observable.prototype.subscribe - // if SubscripitionZone is different with ConstructorZone - // we should run _subscribe in ConstructorZone and - // create sinke in SubscriptionZone, - // and tearDown should also run into ConstructorZone - Observable.prototype.subscribe = function() { - const _zone = this._zone; - const currentZone = Zone.current; - - // if operator is involved, we should also - // patch the call method to save the Subscription zone - if (this.operator && _zone && _zone !== currentZone) { - const call = this.operator.call; - this.operator.call = function() { - const args = Array.prototype.slice.call(arguments); - const subscriber = args.length > 0 ? args[0] : undefined; - if (!subscriber._zone) { - subscriber._zone = currentZone; - } - return _zone.run(call, this, args, subscribeSource); - }; + Object.defineProperties(Observable.prototype, { + _zone: {value: null, writable: true, configurable: true}, + _zoneSource: {value: null, writable: true, configurable: true}, + _zoneSubscribe: {value: null, writable: true, configurable: true}, + source: { + configurable: true, + get: function(this: Observable) { + return (this as any)._zoneSource; + }, + set: function(this: Observable, source: any) { + (this as any)._zone = Zone.current; + (this as any)._zoneSource = source; } - const result = subscribe.apply(this, arguments); - // the result is the subscriber sink, - // we save the current Zone here - if (!result._zone) { - result._zone = currentZone; + }, + _subscribe: { + configurable: true, + get: function(this: Observable) { + if ((this as any)._zoneSubscribe) { + return (this as any)._zoneSubscribe; + } else if (this.constructor === Observable) { + return _subscribe; + } + const proto = Object.getPrototypeOf(this); + return proto && proto._subscribe; + }, + set: function(this: Observable, subscribe: any) { + (this as any)._zone = Zone.current; + (this as any)._zoneSubscribe = subscribe; } - return result; - }; - } - - const symbolLift = symbol('lift'); - if (!ObservablePrototype[symbolLift]) { - const lift = ObservablePrototype[symbolLift] = ObservablePrototype.lift; - - // patch lift method to save ConstructorZone of Observable - Observable.prototype.lift = function() { - const observable = lift.apply(this, arguments); - patchObservableInstance(observable); - - return observable; - }; - } - - const symbolCreate = symbol('create'); - if (!patchedObservable[symbolCreate]) { - const create = patchedObservable[symbolCreate] = Observable.create; - // patch create method to save ConstructorZone of Observable - Rx.Observable.create = function() { - const observable = create.apply(this, arguments); - patchObservableInstance(observable); + }, + subscribe: { + writable: true, + configurable: true, + value: function(this: Observable, observerOrNext: any, error: any, complete: any) { + // Only grab a zone if we Zone exists and it is different from the current zone. + const _zone = (this as any)._zone; + if (_zone && _zone !== Zone.current) { + // Current Zone is different from the intended zone. + // Restore the zone before invoking the subscribe callback. + return _zone.run(subscribe, this, [toSubscriber(observerOrNext, error, complete)]); + } + return subscribe.call(this, observerOrNext, error, complete); + } + } + }); + }; - return observable; - }; - } + const patchSubscription = function() { + const unsubscribeSymbol = symbol('unsubscribe'); + const unsubscribe = (Subscription.prototype as any)[unsubscribeSymbol] = + Subscription.prototype.unsubscribe; + Object.defineProperties(Subscription.prototype, { + _zone: {value: null, writable: true, configurable: true}, + _zoneUnsubscribe: {value: null, writable: true, configurable: true}, + _unsubscribe: { + get: function(this: Subscription) { + if ((this as any)._zoneUnsubscribe) { + return (this as any)._zoneUnsubscribe; + } + const proto = Object.getPrototypeOf(this); + return proto && proto._unsubscribe; + }, + set: function(this: Subscription, unsubscribe: any) { + (this as any)._zone = Zone.current; + (this as any)._zoneUnsubscribe = unsubscribe; + } + }, + unsubscribe: { + writable: true, + configurable: true, + value: function(this: Subscription) { + // Only grab a zone if we Zone exists and it is different from the current zone. + const _zone: Zone = (this as any)._zone; + if (_zone && _zone !== Zone.current) { + // Current Zone is different from the intended zone. + // Restore the zone before invoking the subscribe callback. + _zone.run(unsubscribe, this); + } else { + unsubscribe.apply(this); + } + } + } + }); }; const patchSubscriber = function() { - const Subscriber = Rx.Subscriber; - const next = Subscriber.prototype.next; const error = Subscriber.prototype.error; const complete = Subscriber.prototype.complete; - const unsubscribe = Subscriber.prototype.unsubscribe; + + Object.defineProperty(Subscriber.prototype, 'destination', { + configurable: true, + get: function(this: Subscriber) { + return (this as any)._zoneDestination; + }, + set: function(this: Subscriber, destination: any) { + (this as any)._zone = Zone.current; + (this as any)._zoneDestination = destination; + } + }); // patch Subscriber.next to make sure it run // into SubscriptionZone @@ -196,19 +202,10 @@ import * as Rx from 'rxjs/Rx'; return complete.apply(this, arguments); } }; + }; - Subscriber.prototype.unsubscribe = function() { - const currentZone = Zone.current; - const subscriptionZone = this._zone; - - // for performance concern, check Zone.current - // equal with this._zone(SubscriptionZone) or not - if (subscriptionZone && subscriptionZone !== currentZone) { - return subscriptionZone.run(unsubscribe, this, arguments, unsubscribeSource); - } else { - return unsubscribe.apply(this, arguments); - } - }; + const patchObservableInstance = function(observable: any) { + observable._zone = Zone.current; }; const patchObservableFactoryCreator = function(obj: any, factoryName: string) { @@ -217,6 +214,9 @@ import * as Rx from 'rxjs/Rx'; return; } const factoryCreator: any = obj[symbolFactory] = obj[factoryName]; + if (!factoryCreator) { + return; + } obj[factoryName] = function() { const factory: any = factoryCreator.apply(this, arguments); return function() { @@ -227,8 +227,136 @@ import * as Rx from 'rxjs/Rx'; }; }; - patchObservable(Rx, 'Observable'); + const patchObservableFactory = function(obj: any, factoryName: string) { + const symbolFactory: string = symbol(factoryName); + if (obj[symbolFactory]) { + return; + } + const factory: any = obj[symbolFactory] = obj[factoryName]; + if (!factory) { + return; + } + obj[factoryName] = function() { + const observable = factory.apply(this, arguments); + patchObservableInstance(observable); + return observable; + }; + }; + + const patchObservableFactoryArgs = function(obj: any, factoryName: string) { + const symbolFactory: string = symbol(factoryName); + if (obj[symbolFactory]) { + return; + } + const factory: any = obj[symbolFactory] = obj[factoryName]; + if (!factory) { + return; + } + obj[factoryName] = function() { + const initZone = Zone.current; + const args = Array.prototype.slice.call(arguments); + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (typeof arg === 'function') { + args[i] = function() { + const argArgs = Array.prototype.slice.call(arguments); + const runningZone = Zone.current; + if (initZone && runningZone && initZone !== runningZone) { + return initZone.run(arg, this, argArgs); + } else { + return arg.apply(this, argArgs); + } + }; + } + } + + const observable = factory.apply(this, args); + patchObservableInstance(observable); + return observable; + }; + }; + + const patchMulticast = function() { + const obj: any = Observable.prototype; + const factoryName: string = 'multicast'; + const symbolFactory: string = symbol(factoryName); + if (obj[symbolFactory]) { + return; + } + const factory: any = obj[symbolFactory] = obj[factoryName]; + if (!factory) { + return; + } + obj[factoryName] = function() { + const _zone: any = Zone.current; + const args = Array.prototype.slice.call(arguments); + let subjectOrSubjectFactory: any = args.length > 0 ? args[0] : undefined; + if (typeof subjectOrSubjectFactory !== 'function') { + const originalFactory: any = subjectOrSubjectFactory; + subjectOrSubjectFactory = function() { + return originalFactory; + }; + } + args[0] = function() { + let subject: any; + if (_zone && _zone !== Zone.current) { + subject = _zone.run(subjectOrSubjectFactory, this, arguments); + } else { + subject = subjectOrSubjectFactory.apply(this, arguments); + } + if (subject && _zone) { + subject._zone = _zone; + } + return subject; + }; + const observable = factory.apply(this, args); + patchObservableInstance(observable); + return observable; + }; + }; + + const patchImmediate = function(asap: any) { + if (!asap) { + return; + } + + const scheduleSymbol = symbol('scheduleSymbol'); + const flushSymbol = symbol('flushSymbol'); + const zoneSymbol = symbol('zone'); + if (asap[scheduleSymbol]) { + return; + } + + const schedule = asap[scheduleSymbol] = asap.schedule; + asap.schedule = function() { + const args = Array.prototype.slice.call(arguments); + const work = args.length > 0 ? args[0] : undefined; + const delay = args.length > 1 ? args[1] : 0; + const state = (args.length > 2 ? args[2] : undefined) || {}; + state[zoneSymbol] = Zone.current; + + const patchedWork = function() { + const workArgs = Array.prototype.slice.call(arguments); + const action = workArgs.length > 0 ? workArgs[0] : undefined; + const scheduleZone = action && action[zoneSymbol]; + if (scheduleZone && scheduleZone !== Zone.current) { + return scheduleZone.run(work, this, arguments); + } else { + return work.apply(this, arguments); + } + }; + return schedule.apply(this, [patchedWork, delay, state]); + }; + }; + + patchObservable(); + patchSubscription(); patchSubscriber(); - patchObservableFactoryCreator(Rx.Observable, 'bindCallback'); - patchObservableFactoryCreator(Rx.Observable, 'bindNodeCallback'); + patchObservableFactoryCreator(Observable, 'bindCallback'); + patchObservableFactoryCreator(Observable, 'bindNodeCallback'); + patchObservableFactory(Observable, 'defer'); + patchObservableFactory(Observable, 'forkJoin'); + patchObservableFactoryArgs(Observable, 'fromEventPattern'); + patchMulticast(); + patchImmediate(asap); }); \ No newline at end of file diff --git a/test/browser-zone-setup.ts b/test/browser-zone-setup.ts index 57c641595..1c16e96a8 100644 --- a/test/browser-zone-setup.ts +++ b/test/browser-zone-setup.ts @@ -17,5 +17,4 @@ import '../lib/zone-spec/proxy'; import '../lib/zone-spec/sync-test'; import '../lib/zone-spec/task-tracking'; import '../lib/zone-spec/wtf'; -import '../lib/extra/cordova'; -import '../lib/rxjs/rxjs'; \ No newline at end of file +import '../lib/extra/cordova'; \ No newline at end of file diff --git a/test/global-rxjs.ts b/test/global-rxjs.ts deleted file mode 100644 index f012a136a..000000000 --- a/test/global-rxjs.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -const globalRx: any = (window as any).Rx; -exports.Observable = globalRx.Observable; -exports.Subject = globalRx.Subject; -exports.Scheduler = globalRx.Scheduler; \ No newline at end of file diff --git a/test/main.ts b/test/main.ts index 871994246..117e274e6 100644 --- a/test/main.ts +++ b/test/main.ts @@ -14,19 +14,15 @@ declare const __karma__: { __karma__.loaded = function() {}; (window as any).global = window; +System.config({ + defaultJSExtensions: true, + map: {'rxjs': 'base/node_modules/rxjs'}, +}); let browserPatchedPromise: any = null; if ((window as any)[(Zone as any).__symbol__('setTimeout')]) { - System.config({ - defaultJSExtensions: true, - map: {'rxjs/Rx': 'base/build/test/global-rxjs.js'}, - }); browserPatchedPromise = Promise.resolve('browserPatched'); } else { - System.config({ - defaultJSExtensions: true, - map: {'rxjs/Rx': 'base/node_modules/rxjs/bundles/Rx.js'}, - }); // this means that Zone has not patched the browser yet, which means we must be running in // build mode and need to load the browser patch. browserPatchedPromise = System.import('/base/build/test/browser-zone-setup'); diff --git a/test/rxjs/rxjs.Observable.audit.spec.ts b/test/rxjs/rxjs.Observable.audit.spec.ts new file mode 100644 index 000000000..3007d8d0d --- /dev/null +++ b/test/rxjs/rxjs.Observable.audit.spec.ts @@ -0,0 +1,83 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.audit', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('audit func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.interval(100); + return source.audit(ev => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return Rx.Observable.interval(150); + }); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + if (result >= 3) { + subscriber.complete(); + } + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 3, 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); + + xit('auditTime func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.interval(100); + return source.auditTime(360); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + if (result >= 7) { + subscriber.complete(); + } + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([3, 7, 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.buffer.spec.ts b/test/rxjs/rxjs.Observable.buffer.spec.ts new file mode 100644 index 000000000..c134e71bb --- /dev/null +++ b/test/rxjs/rxjs.Observable.buffer.spec.ts @@ -0,0 +1,182 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +xdescribe('Observable.buffer', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('buffer func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.interval(350); + const interval = Rx.Observable.interval(100); + return interval.buffer(source); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + if (result[0] >= 3) { + subscriber.complete(); + } + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([[0, 1, 2], [3, 4, 5], 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); + + it('bufferCount func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const interval = Rx.Observable.interval(100); + return interval.bufferCount(3); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + if (result[0] >= 3) { + subscriber.complete(); + } + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([[0, 1, 2], [3, 4, 5], 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); + + it('bufferTime func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const interval = Rx.Observable.interval(100); + return interval.bufferTime(350); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + if (result[0] >= 3) { + subscriber.complete(); + } + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([[0, 1, 2], [3, 4, 5], 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); + + it('bufferToggle func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.interval(10); + const opening = Rx.Observable.interval(25); + const closingSelector = (v: any) => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return v % 2 === 0 ? Rx.Observable.of(v) : Rx.Observable.empty(); + }; + return source.bufferToggle(opening, closingSelector); + }); + + let i = 0; + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + subscriber.complete(); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([[], 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); + + it('bufferWhen func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.interval(100); + return source.bufferWhen(() => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return Rx.Observable.interval(220); + }); + }); + + let i = 0; + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + if (i++ >= 3) { + subscriber.complete(); + } + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([[0, 1], [2, 3], [4, 5], [6, 7], 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.catch.spec.ts b/test/rxjs/rxjs.Observable.catch.spec.ts new file mode 100644 index 000000000..09a1e8270 --- /dev/null +++ b/test/rxjs/rxjs.Observable.catch.spec.ts @@ -0,0 +1,86 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +describe('Observable.catch', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('catch func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const error = new Error('test'); + const source = Rx.Observable.of(1, 2, 3).map((n: number) => { + expect(Zone.current.name).toEqual(constructorZone1.name); + if (n === 2) { + throw error; + } + return n; + }); + return source.catch((err: any) => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return Rx.Observable.of('error1', 'error2'); + }); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + expect(log).toEqual([1, 'error1', 'error2', 'completed']); + }); + + it('retry func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3) + .map((n: number) => { + expect(Zone.current.name).toEqual(constructorZone1.name); + if (n === 2) { + throw error; + } + return n; + }) + .retry(1); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + (error: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(error); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + expect(log).toEqual([1, 1, error]); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.collection.spec.ts b/test/rxjs/rxjs.Observable.collection.spec.ts new file mode 100644 index 000000000..00bd1d130 --- /dev/null +++ b/test/rxjs/rxjs.Observable.collection.spec.ts @@ -0,0 +1,735 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.collection', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('elementAt func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).elementAt(1); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 'completed']); + }); + }); + }); + + it('every func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const everyZone1: Zone = Zone.current.fork({name: 'Every Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + observable1 = everyZone1.run(() => { + return observable1.every((v: any) => { + expect(Zone.current.name).toEqual(everyZone1.name); + return v % 2 === 0; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([false, 'completed']); + }); + }); + }); + + it('filter func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const filterZone1: Zone = Zone.current.fork({name: 'Filter Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + observable1 = filterZone1.run(() => { + return observable1.filter((v: any) => { + expect(Zone.current.name).toEqual(filterZone1.name); + return v % 2 === 0; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 'completed']); + }); + }); + }); + + it('find func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const findZone1: Zone = Zone.current.fork({name: 'Find Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + observable1 = findZone1.run(() => { + return observable1.find((v: any) => { + expect(Zone.current.name).toEqual(findZone1.name); + return v === 2; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 'completed']); + }); + }); + }); + + it('findIndex func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const findZone1: Zone = Zone.current.fork({name: 'Find Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + observable1 = findZone1.run(() => { + return observable1.findIndex((v: any) => { + expect(Zone.current.name).toEqual(findZone1.name); + return v === 2; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 'completed']); + }); + }); + }); + + it('first func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const firstZone1: Zone = Zone.current.fork({name: 'First Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + observable1 = firstZone1.run(() => { + return observable1.first((v: any) => { + expect(Zone.current.name).toEqual(firstZone1.name); + return v === 2; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 'completed']); + }); + }); + }); + + it('groupBy func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const groupByZone1: Zone = Zone.current.fork({name: 'groupBy Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + const people = [ + {name: 'Sue', age: 25}, {name: 'Joe', age: 30}, {name: 'Frank', age: 25}, + {name: 'Sarah', age: 35} + ]; + return Rx.Observable.from(people); + }); + + observable1 = groupByZone1.run(() => { + return observable1 + .groupBy((person: any) => { + expect(Zone.current.name).toEqual(groupByZone1.name); + return person.age; + }) + // return as array of each group + .flatMap((group: any) => group.reduce((acc: any, curr: any) => [...acc, curr], [])); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([ + [{age: 25, name: 'Sue'}, {age: 25, name: 'Frank'}], [{age: 30, name: 'Joe'}], + [{age: 35, name: 'Sarah'}], 'completed' + ]); + }); + }); + }); + + it('ignoreElements func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const ignoreZone1: Zone = Zone.current.fork({name: 'Ignore Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).ignoreElements(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + fail('should not call next'); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual(['completed']); + }); + }); + }); + + it('isEmpty func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const isEmptyZone1: Zone = Zone.current.fork({name: 'IsEmpty Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).isEmpty(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([false, 'completed']); + }); + }); + }); + + it('last func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const lastZone1: Zone = Zone.current.fork({name: 'Last Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).last(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([3, 'completed']); + }); + }); + }); + + it('map func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const mapZone1: Zone = Zone.current.fork({name: 'Map Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + observable1 = mapZone1.run(() => { + return observable1.map((v: any) => { + expect(Zone.current.name).toEqual(mapZone1.name); + return v + 1; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 3, 4, 'completed']); + }); + }); + }); + + it('mapTo func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const mapToZone1: Zone = Zone.current.fork({name: 'MapTo Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + observable1 = mapToZone1.run(() => { + return observable1.mapTo('a'); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual(['a', 'a', 'a', 'completed']); + }); + }); + }); + + it('max func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(4, 2, 3).max(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([4, 'completed']); + }); + }); + }); + + it('max with comparer func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const maxZone1: Zone = Zone.current.fork({name: 'Max Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(4, 2, 3); + }); + + observable1 = maxZone1.run(() => { + return observable1.max((x: number, y: number) => { + expect(Zone.current.name).toEqual(maxZone1.name); + return x < y ? -1 : 1; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([4, 'completed']); + }); + }); + }); + + it('min func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(4, 2, 3).min(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 'completed']); + }); + }); + }); + + it('min with comparer func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const minZone1: Zone = Zone.current.fork({name: 'Min Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(4, 2, 3); + }); + + observable1 = minZone1.run(() => { + return observable1.max((x: number, y: number) => { + expect(Zone.current.name).toEqual(minZone1.name); + return x < y ? 1 : -1; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 'completed']); + }); + }); + }); + + it('reduce func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const reduceZone1: Zone = Zone.current.fork({name: 'Min Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(4, 2, 3); + }); + + observable1 = reduceZone1.run(() => { + return observable1.reduce((acc: number, one: number) => { + expect(Zone.current.name).toEqual(reduceZone1.name); + return acc + one; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([9, 'completed']); + }); + }); + }); + + it('scan func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const scanZone1: Zone = Zone.current.fork({name: 'Min Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(4, 2, 3); + }); + + observable1 = scanZone1.run(() => { + return observable1.scan((acc: number, one: number) => { + expect(Zone.current.name).toEqual(scanZone1.name); + return acc + one; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([4, 6, 9, 'completed']); + }); + }); + }); + + it('repeat func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1).repeat(2); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 1, 'completed']); + }); + }); + }); + + it('single func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const singleZone1: Zone = Zone.current.fork({name: 'Single Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3, 4, 5); + }); + + observable1 = singleZone1.run(() => { + return observable1.single((val: any) => { + expect(Zone.current.name).toEqual(singleZone1.name); + return val === 4; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([4, 'completed']); + }); + }); + }); + + it('skip func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3, 4, 5).skip(3); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([4, 5, 'completed']); + }); + }); + }); + + xit('skipUntil func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.interval(10).skipUntil(Rx.Observable.interval(25)); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + subscriber.complete(); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('skipWhile func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const skipZone1: Zone = Zone.current.fork({name: 'Skip Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.interval(10); + }); + + observable1 = skipZone1.run(() => { + return observable1.skipWhile((val: any) => { + expect(Zone.current.name).toEqual(skipZone1.name); + return val < 2; + }); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + subscriber.complete(); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('startWith func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2).startWith(3); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([3, 1, 2, 'completed']); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.combine.spec.ts b/test/rxjs/rxjs.Observable.combine.spec.ts new file mode 100644 index 000000000..c524637f8 --- /dev/null +++ b/test/rxjs/rxjs.Observable.combine.spec.ts @@ -0,0 +1,148 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.combine', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('combineAll func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.interval(10); + const highOrder = source + .map((src: any) => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return Rx.Observable.interval(50).take(3); + }) + .take(2); + return highOrder.combineAll(); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([[0, 0], [1, 0], [1, 1], [2, 1], [2, 2], 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); + + it('combineAll func callback should run in the correct zone with project function', + asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.interval(10); + const highOrder = source + .map((src: any) => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return Rx.Observable.interval(50).take(3); + }) + .take(2); + return highOrder.combineAll((x: any, y: any) => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return {x: x, y: y}; + }); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([ + {x: 0, y: 0}, {x: 1, y: 0}, {x: 1, y: 1}, {x: 2, y: 1}, {x: 2, y: 2}, 'completed' + ]); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); + + it('combineLatest func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.of(1, 2, 3); + const input = Rx.Observable.of(4, 5, 6); + return source.combineLatest(input); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + + expect(log).toEqual([[3, 4], [3, 5], [3, 6], 'completed']); + }); + + it('combineLatest func callback should run in the correct zone with project function', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.of(1, 2, 3); + const input = Rx.Observable.of(4, 5, 6); + return source.combineLatest(input, function(x: any, y: any) { + return x + y; + }); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + + expect(log).toEqual([7, 8, 9, 'completed']); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.concat.spec.ts b/test/rxjs/rxjs.Observable.concat.spec.ts new file mode 100644 index 000000000..393933606 --- /dev/null +++ b/test/rxjs/rxjs.Observable.concat.spec.ts @@ -0,0 +1,203 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable instance method concat', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const constructorZone2: Zone = Zone.current.fork({name: 'Constructor Zone2'}); + const constructorZone3: Zone = Zone.current.fork({name: 'Constructor Zone3'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + let observable2: any; + + let concatObservable: any; + + beforeEach(() => { + log = []; + }); + + it('concat func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return new Rx.Observable(subscriber => { + expect(Zone.current.name).toEqual(constructorZone1.name); + subscriber.next(1); + subscriber.next(2); + subscriber.complete(); + }); + }); + + observable2 = constructorZone2.run(() => { + return Rx.Observable.range(3, 4); + }); + + constructorZone3.run(() => { + concatObservable = observable1.concat(observable2); + }); + + subscriptionZone.run(() => { + concatObservable.subscribe((concat: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(concat); + }); + }); + + expect(log).toEqual([1, 2, 3, 4, 5, 6]); + }); + + xit('concat func callback should run in the correct zone with scheduler', + asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const constructorZone2: Zone = Zone.current.fork({name: 'Constructor Zone2'}); + const constructorZone3: Zone = Zone.current.fork({name: 'Constructor Zone3'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2); + }); + + observable2 = constructorZone2.run(() => { + return Rx.Observable.range(3, 4); + }); + + constructorZone3.run(() => { + concatObservable = observable1.concat(observable2, Rx.Scheduler.asap); + }); + + subscriptionZone.run(() => { + concatObservable.subscribe( + (concat: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(concat); + }, + (error: any) => { + fail('subscribe failed' + error); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 2, 3, 4, 5, 6]); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); + + it('concatAll func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const constructorZone2: Zone = Zone.current.fork({name: 'Constructor Zone2'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(0, 1, 2); + }); + + constructorZone2.run(() => { + const highOrder = observable1.map((v: any) => { + expect(Zone.current.name).toEqual(constructorZone2.name); + return Rx.Observable.of(v + 1); + }); + concatObservable = highOrder.concatAll(); + }); + + subscriptionZone.run(() => { + concatObservable.subscribe( + (concat: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(concat); + }, + (error: any) => { + fail('subscribe failed' + error); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 2, 3]); + done(); + }); + }); + }, Zone.root)); + + it('concatMap func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const constructorZone2: Zone = Zone.current.fork({name: 'Constructor Zone2'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return new Rx.Observable(subscriber => { + expect(Zone.current.name).toEqual(constructorZone1.name); + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + subscriber.next(4); + subscriber.complete(); + }); + }); + + constructorZone2.run(() => { + concatObservable = observable1.concatMap((v: any) => { + expect(Zone.current.name).toEqual(constructorZone2.name); + return Rx.Observable.interval(10).take(2); + }); + }); + + subscriptionZone.run(() => { + concatObservable.subscribe( + (concat: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(concat); + }, + (error: any) => { + fail('subscribe failed' + error); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 0, 1, 0, 1, 0, 1]); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); + + it('concatMapTo func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const constructorZone2: Zone = Zone.current.fork({name: 'Constructor Zone2'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return new Rx.Observable(subscriber => { + expect(Zone.current.name).toEqual(constructorZone1.name); + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + subscriber.next(4); + subscriber.complete(); + }); + }); + + constructorZone2.run(() => { + concatObservable = observable1.concatMapTo(Rx.Observable.interval(10).take(2)); + }); + + subscriptionZone.run(() => { + concatObservable.subscribe( + (concat: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(concat); + }, + (error: any) => { + fail('subscribe failed' + error); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 0, 1, 0, 1, 0, 1]); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.count.spec.ts b/test/rxjs/rxjs.Observable.count.spec.ts new file mode 100644 index 000000000..4bb7ab8bb --- /dev/null +++ b/test/rxjs/rxjs.Observable.count.spec.ts @@ -0,0 +1,44 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +describe('Observable.count', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('count func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.range(1, 3).count((i: number) => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return i % 2 === 0; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + expect(log).toEqual([1, 'completed']); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.debounce.spec.ts b/test/rxjs/rxjs.Observable.debounce.spec.ts new file mode 100644 index 000000000..36666e457 --- /dev/null +++ b/test/rxjs/rxjs.Observable.debounce.spec.ts @@ -0,0 +1,71 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.debounce', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('debounce func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).debounce(() => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return Rx.Observable.timer(100); + }); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + done(); + }); + }); + expect(log).toEqual([3, 'completed']); + }, Zone.root)); + + it('debounceTime func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).debounceTime(100); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + done(); + }); + }); + expect(log).toEqual([3, 'completed']); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.default.spec.ts b/test/rxjs/rxjs.Observable.default.spec.ts new file mode 100644 index 000000000..626c2fd78 --- /dev/null +++ b/test/rxjs/rxjs.Observable.default.spec.ts @@ -0,0 +1,45 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.defaultIfEmpty', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('defaultIfEmpty func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.interval(100) + .takeUntil(Rx.Observable.timer(50)) + .defaultIfEmpty('empty'); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual(['empty', 'completed']); + done(); + }); + }); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.delay.spec.ts b/test/rxjs/rxjs.Observable.delay.spec.ts new file mode 100644 index 000000000..289e8c362 --- /dev/null +++ b/test/rxjs/rxjs.Observable.delay.spec.ts @@ -0,0 +1,70 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.delay', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('delay func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).delay(100); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 2, 3, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('delayWhen func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).delayWhen((v: any) => { + return Rx.Observable.timer(v * 10); + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 2, 3, 'completed']); + done(); + }); + }); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.distinct.spec.ts b/test/rxjs/rxjs.Observable.distinct.spec.ts new file mode 100644 index 000000000..6e5534186 --- /dev/null +++ b/test/rxjs/rxjs.Observable.distinct.spec.ts @@ -0,0 +1,96 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +describe('Observable.distinct', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('distinct func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 1, 2, 2, 2, 1, 2, 3, 4, 3, 2, 1).distinct(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + expect(log).toEqual([1, 2, 3, 4, 'completed']); + }); + + it('distinctUntilChanged func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 1, 2, 2, 2, 1, 1, 2, 3, 3, 4).distinctUntilChanged(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + expect(log).toEqual([1, 2, 1, 2, 3, 4, 'completed']); + }); + + it('distinctUntilKeyChanged func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable + .of({age: 4, name: 'Foo'}, {age: 7, name: 'Bar'}, {age: 5, name: 'Foo'}, + {age: 6, name: 'Foo'}) + .distinctUntilKeyChanged('name'); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + expect(log).toEqual( + [{age: 4, name: 'Foo'}, {age: 7, name: 'Bar'}, {age: 5, name: 'Foo'}, 'completed']); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.do.spec.ts b/test/rxjs/rxjs.Observable.do.spec.ts new file mode 100644 index 000000000..3cbb3d4cd --- /dev/null +++ b/test/rxjs/rxjs.Observable.do.spec.ts @@ -0,0 +1,50 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +describe('Observable.do', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('do func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const doZone1: Zone = Zone.current.fork({name: 'Do Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1); + }); + + observable1 = doZone1.run(() => { + return observable1.do((v: any) => { + log.push(v); + expect(Zone.current.name).toEqual(doZone1.name); + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push('result' + result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 'result1', 'completed']); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.map.spec.ts b/test/rxjs/rxjs.Observable.map.spec.ts new file mode 100644 index 000000000..11c6ebe59 --- /dev/null +++ b/test/rxjs/rxjs.Observable.map.spec.ts @@ -0,0 +1,109 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +describe('Observable.map', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('pairwise func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).pairwise(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + expect(log).toEqual([[1, 2], [2, 3], 'completed']); + }); + + it('partition func callback should run in the correct zone', () => { + const partitionZone = Zone.current.fork({name: 'Partition Zone1'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + const part: any = partitionZone.run(() => { + return observable1.partition((val: any) => { + expect(Zone.current.name).toEqual(partitionZone.name); + return val % 2 === 0; + }); + }); + + subscriptionZone.run(() => { + part[0].subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('first' + result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + + part[1].subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('second' + result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + expect(log).toEqual(['first2', 'completed', 'second1', 'second3', 'completed']); + }); + + it('pluck func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.of({a: 1, b: 2}, {a: 3, b: 4}).pluck('a'); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + expect(log).toEqual([1, 3, 'completed']); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.merge.spec.ts b/test/rxjs/rxjs.Observable.merge.spec.ts new file mode 100644 index 000000000..bb940cae8 --- /dev/null +++ b/test/rxjs/rxjs.Observable.merge.spec.ts @@ -0,0 +1,240 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.merge', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('expand func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const expandZone1: Zone = Zone.current.fork({name: 'Expand Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(2); + }); + + observable1 = expandZone1.run(() => { + return observable1 + .expand((val: any) => { + expect(Zone.current.name).toEqual(expandZone1.name); + return Rx.Observable.of(1 + val); + }) + .take(2); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + expect(log).toEqual([2, 3, 'completed']); + }); + + it('merge func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.merge( + Rx.Observable.interval(10).take(2), Rx.Observable.interval(15).take(1)); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 0, 1, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('mergeAll func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2) + .map((v: any) => { + return Rx.Observable.of(v + 1); + }) + .mergeAll(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 3, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('mergeMap func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2).mergeMap((v: any) => { + return Rx.Observable.of(v + 1); + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([2, 3, 'completed']); + }); + }); + }); + + it('mergeMapTo func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2).mergeMapTo(Rx.Observable.of(10)); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([10, 10, 'completed']); + }); + }); + }); + + it('switch func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.range(0, 3) + .map(function(x: any) { + return Rx.Observable.range(x, 3); + }) + .switch (); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 2, 1, 2, 3, 2, 3, 4, 'completed']); + }); + }); + }); + + it('switchMap func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.range(0, 3).switchMap(function(x: any) { + return Rx.Observable.range(x, 3); + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 2, 1, 2, 3, 2, 3, 4, 'completed']); + }); + }); + }); + + it('switchMapTo func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.range(0, 3).switchMapTo('a'); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual(['a', 'a', 'a', 'completed']); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.multicast.spec.ts b/test/rxjs/rxjs.Observable.multicast.spec.ts new file mode 100644 index 000000000..2c1cb65f0 --- /dev/null +++ b/test/rxjs/rxjs.Observable.multicast.spec.ts @@ -0,0 +1,78 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +// TODO: @JiaLiPassion, Observable.prototype.multicast return a readonly _subscribe +// should find another way to patch subscribe +describe('Observable.multicast', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const doZone1: Zone = Zone.current.fork({name: 'Do Zone1'}); + const mapZone1: Zone = Zone.current.fork({name: 'Map Zone1'}); + const multicastZone1: Zone = Zone.current.fork({name: 'Multicast Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('multicast func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + observable1 = doZone1.run(() => { + return observable1.do((v: any) => { + expect(Zone.current.name).toEqual(doZone1.name); + log.push('do' + v); + }); + }); + + observable1 = mapZone1.run(() => { + return observable1.mapTo('test'); + }); + + const multi: any = multicastZone1.run(() => { + return observable1.multicast(() => { + expect(Zone.current.name).toEqual(multicastZone1.name); + return new Rx.Subject(); + }); + }); + + multi.subscribe((val: any) => { + log.push('one' + val); + }); + + multi.subscribe((val: any) => { + log.push('two' + val); + }); + + multi.connect(); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + + expect(log).toEqual([ + 'do1', 'onetest', 'twotest', 'do2', 'onetest', 'twotest', 'do3', 'onetest', 'twotest', 'do1', + 'test', 'do2', 'test', 'do3', 'test', 'completed' + ]); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.notification.spec.ts b/test/rxjs/rxjs.Observable.notification.spec.ts new file mode 100644 index 000000000..6bc561005 --- /dev/null +++ b/test/rxjs/rxjs.Observable.notification.spec.ts @@ -0,0 +1,54 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest, ifEnvSupports} from '../test-util'; + +const supportNotification = function() { + return typeof Rx.Notification !== 'undefined'; +}; + +(supportNotification as any).message = 'RxNotification'; + +describe('Observable.notification', ifEnvSupports(supportNotification, () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('notification func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + const notifA = new Rx.Notification('N', 'A'); + const notifB = new Rx.Notification('N', 'B'); + const notifE = new Rx.Notification('E', void 0, error); + const materialized = Rx.Observable.of(notifA, notifB, notifE); + return materialized.dematerialize(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + log.push(err); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual(['A', 'B', error]); + }); + }); + }); + })); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.race.spec.ts b/test/rxjs/rxjs.Observable.race.spec.ts new file mode 100644 index 000000000..44369d44d --- /dev/null +++ b/test/rxjs/rxjs.Observable.race.spec.ts @@ -0,0 +1,45 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.race', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('race func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.race( + Rx.Observable.interval(10).mapTo('a'), Rx.Observable.interval(15).mapTo('b')); + }); + + subscriptionZone.run(() => { + const subscriber: any = observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + subscriber.complete(); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual(['a', 'completed']); + done(); + }); + }); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.sample.spec.ts b/test/rxjs/rxjs.Observable.sample.spec.ts new file mode 100644 index 000000000..2f767df4d --- /dev/null +++ b/test/rxjs/rxjs.Observable.sample.spec.ts @@ -0,0 +1,72 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.sample', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('sample func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.interval(10).sample(Rx.Observable.interval(15)); + }); + + subscriptionZone.run(() => { + const subscriber: any = observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + subscriber.complete(); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 'completed']); + done(); + }); + }); + }, Zone.root)); + + xit('throttle func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.interval(10).take(5).throttle((val: any) => { + expect(Zone.current.name).toEqual(constructorZone1.name); + return Rx.Observable.interval(20); + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 2, 4, 'completed']); + done(); + }); + }); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.take.spec.ts b/test/rxjs/rxjs.Observable.take.spec.ts new file mode 100644 index 000000000..42aed8445 --- /dev/null +++ b/test/rxjs/rxjs.Observable.take.spec.ts @@ -0,0 +1,124 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.take', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('take func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).take(1); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 'completed']); + }); + }); + }); + + it('takeLast func callback should run in the correct zone', () => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3).takeLast(1); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([3, 'completed']); + }); + }); + }); + + xit('takeUntil func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.interval(10).takeUntil(Rx.Observable.interval(25)); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('takeWhile func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const takeZone1: Zone = Zone.current.fork({name: 'Take Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.interval(10); + }); + + observable1 = takeZone1.run(() => { + return observable1.takeWhile((val: any) => { + expect(Zone.current.name).toEqual(takeZone1.name); + return val < 2; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 'completed']); + done(); + }); + }); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.timeout.spec.ts b/test/rxjs/rxjs.Observable.timeout.spec.ts new file mode 100644 index 000000000..28e7ccfbf --- /dev/null +++ b/test/rxjs/rxjs.Observable.timeout.spec.ts @@ -0,0 +1,63 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.timeout', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('timeout func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1).timeout(10); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('promise should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const promise: any = constructorZone1.run(() => { + return Rx.Observable.of(1).toPromise(); + }); + + subscriptionZone.run(() => { + promise.then( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(result).toEqual(1); + done(); + }, + (err: any) => { + fail('should not call error'); + }); + }); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.Observable.window.spec.ts b/test/rxjs/rxjs.Observable.window.spec.ts new file mode 100644 index 000000000..313baf4ed --- /dev/null +++ b/test/rxjs/rxjs.Observable.window.spec.ts @@ -0,0 +1,148 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.window', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('window func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.timer(0, 10).take(6); + const window = source.window(Rx.Observable.interval(30)); + return window.mergeAll(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 2, 3, 4, 5, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('windowCount func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + const source = Rx.Observable.timer(0, 10).take(10); + const window = source.windowCount(4); + return window.mergeAll(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('windowToggle func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const windowZone1: Zone = Zone.current.fork({name: 'Window Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.timer(0, 10).take(10); + }); + + windowZone1.run(() => { + return observable1 + .windowToggle( + Rx.Observable.interval(30), + (val: any) => { + expect(Zone.current.name).toEqual(windowZone1.name); + return Rx.Observable.interval(15); + }) + .mergeAll(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'completed']); + done(); + }); + }); + }, Zone.root)); + + it('windowWhen func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const windowZone1: Zone = Zone.current.fork({name: 'Window Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.timer(0, 10).take(10); + }); + + windowZone1.run(() => { + return observable1 + .windowWhen((val: any) => { + expect(Zone.current.name).toEqual(windowZone1.name); + return Rx.Observable.interval(15); + }) + .mergeAll(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + (err: any) => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'completed']); + done(); + }); + }); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.bindCallback.spec.ts b/test/rxjs/rxjs.bindCallback.spec.ts index 014a7b503..fb52dea78 100644 --- a/test/rxjs/rxjs.bindCallback.spec.ts +++ b/test/rxjs/rxjs.bindCallback.spec.ts @@ -48,6 +48,7 @@ describe('Observable.bindCallback', () => { callback(arg0); }; boundFunc = Rx.Observable.bindCallback(func, (arg: any) => { + expect(Zone.current.name).toEqual(constructorZone.name); return 'selector' + arg; }); observable = boundFunc('test'); @@ -63,24 +64,24 @@ describe('Observable.bindCallback', () => { expect(log).toEqual(['nextselectortest']); }); - xit('bindCallback with async scheduler should run in correct zone', asyncTest((done: any) => { - constructorZone.run(() => { - func = function(arg0: any, callback: Function) { - expect(Zone.current.name).toEqual(constructorZone.name); - callback(arg0); - }; - boundFunc = Rx.Observable.bindCallback(func, null, Rx.Scheduler.asap); - observable = boundFunc('test'); - }); + it('bindCallback with async scheduler should run in correct zone', asyncTest((done: any) => { + constructorZone.run(() => { + func = function(arg0: any, callback: Function) { + expect(Zone.current.name).toEqual(constructorZone.name); + callback(arg0); + }; + boundFunc = Rx.Observable.bindCallback(func, null, Rx.Scheduler.asap); + observable = boundFunc('test'); + }); - subscriptionZone.run(() => { - observable.subscribe((arg: any) => { - expect(Zone.current.name).toEqual(subscriptionZone.name); - log.push('next' + arg); - done(); - }); - }); + subscriptionZone.run(() => { + observable.subscribe((arg: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('next' + arg); + done(); + }); + }); - expect(log).toEqual([]); - }, Zone.root)); + expect(log).toEqual([]); + }, Zone.root)); }); \ No newline at end of file diff --git a/test/rxjs/rxjs.bindNodeCallback.spec.ts b/test/rxjs/rxjs.bindNodeCallback.spec.ts index 26ea5e1b2..90c8002f8 100644 --- a/test/rxjs/rxjs.bindNodeCallback.spec.ts +++ b/test/rxjs/rxjs.bindNodeCallback.spec.ts @@ -48,6 +48,7 @@ describe('Observable.bindNodeCallback', () => { callback(null, arg); }; boundFunc = Rx.Observable.bindNodeCallback(func, (arg: any) => { + expect(Zone.current.name).toEqual(constructorZone.name); return 'selector' + arg; }); observable = boundFunc('test'); @@ -63,26 +64,26 @@ describe('Observable.bindNodeCallback', () => { expect(log).toEqual(['nextselectortest']); }); - xit('bindNodeCallback with async scheduler should run in correct zone', asyncTest((done: any) => { - constructorZone.run(() => { - func = function(arg: any, callback: (error: any, result: any) => any) { - expect(Zone.current.name).toEqual(constructorZone.name); - callback(null, arg); - }; - boundFunc = Rx.Observable.bindCallback(func, null, Rx.Scheduler.asap); - observable = boundFunc('test'); - }); + it('bindNodeCallback with async scheduler should run in correct zone', asyncTest((done: any) => { + constructorZone.run(() => { + func = function(arg: any, callback: (error: any, result: any) => any) { + expect(Zone.current.name).toEqual(constructorZone.name); + callback(null, arg); + }; + boundFunc = Rx.Observable.bindCallback(func, null, Rx.Scheduler.asap); + observable = boundFunc('test'); + }); - subscriptionZone.run(() => { - observable.subscribe((arg: any) => { - expect(Zone.current.name).toEqual(subscriptionZone.name); - log.push('next' + arg); - done(); - }); - }); + subscriptionZone.run(() => { + observable.subscribe((arg: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('next' + arg); + done(); + }); + }); - expect(log).toEqual([]); - })); + expect(log).toEqual([]); + })); it('bindNodeCallback call with error should run in correct zone', () => { constructorZone.run(() => { diff --git a/test/rxjs/rxjs.combineLatest.spec.ts b/test/rxjs/rxjs.combineLatest.spec.ts index e4d6e249e..07406fc43 100644 --- a/test/rxjs/rxjs.combineLatest.spec.ts +++ b/test/rxjs/rxjs.combineLatest.spec.ts @@ -25,7 +25,7 @@ describe('Observable.combineLatest', () => { log = []; }); - it('bindCallback func callback should run in the correct zone', () => { + it('combineLatest func should run in the correct zone', () => { observable1 = constructorZone1.run(() => new Rx.Observable((_subscriber) => { subscriber1 = _subscriber; expect(Zone.current.name).toEqual(constructorZone1.name); @@ -54,4 +54,38 @@ describe('Observable.combineLatest', () => { expect(log).toEqual(['setup1', 'setup2', [1, 2], [1, 3]]); }); + + it('combineLatest func with project function should run in the correct zone', () => { + observable1 = constructorZone1.run(() => new Rx.Observable((_subscriber) => { + subscriber1 = _subscriber; + expect(Zone.current.name).toEqual(constructorZone1.name); + log.push('setup1'); + })); + observable2 = constructorZone2.run(() => new Rx.Observable((_subscriber) => { + subscriber2 = _subscriber; + expect(Zone.current.name).toEqual(constructorZone2.name); + log.push('setup2'); + })); + + constructorZone3.run(() => { + combinedObservable = + Rx.Observable.combineLatest(observable1, observable2, (x: number, y: number) => { + expect(Zone.current.name).toEqual(constructorZone3.name); + return x + y; + }); + }); + + subscriptionZone.run(() => { + combinedObservable.subscribe((combined: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(combined); + }); + }); + + subscriber1.next(1); + subscriber2.next(2); + subscriber2.next(3); + + expect(log).toEqual(['setup1', 'setup2', 3, 4]); + }); }); \ No newline at end of file diff --git a/test/rxjs/rxjs.common.spec.ts b/test/rxjs/rxjs.common.spec.ts index 106321d96..eba01e333 100644 --- a/test/rxjs/rxjs.common.spec.ts +++ b/test/rxjs/rxjs.common.spec.ts @@ -144,6 +144,7 @@ describe('Zone interaction', () => { const log: string[] = []; const constructorZone: Zone = Zone.current.fork({name: 'Constructor Zone'}); let observable: any = constructorZone.run(() => Rx.Observable.create((subscriber: any) => { + expect(Zone.current.name).toEqual(constructorZone.name); subscriber.next(1); subscriber.complete(); return () => { diff --git a/test/rxjs/rxjs.concat.spec.ts b/test/rxjs/rxjs.concat.spec.ts new file mode 100644 index 000000000..da7c5c8f1 --- /dev/null +++ b/test/rxjs/rxjs.concat.spec.ts @@ -0,0 +1,96 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.concat', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const constructorZone2: Zone = Zone.current.fork({name: 'Constructor Zone2'}); + const constructorZone3: Zone = Zone.current.fork({name: 'Constructor Zone3'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + let observable2: any; + + let concatObservable: any; + + beforeEach(() => { + log = []; + }); + + it('concat func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return new Rx.Observable(subscriber => { + expect(Zone.current.name).toEqual(constructorZone1.name); + subscriber.next(1); + subscriber.next(2); + subscriber.complete(); + }); + }); + + observable2 = constructorZone2.run(() => { + return Rx.Observable.range(3, 4); + }); + + constructorZone3.run(() => { + concatObservable = Rx.Observable.concat(observable1, observable2); + }); + + subscriptionZone.run(() => { + concatObservable.subscribe((concat: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(concat); + }); + }); + + expect(log).toEqual([1, 2, 3, 4, 5, 6]); + }); + + it('concat func callback should run in the correct zone with scheduler', + asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const constructorZone2: Zone = Zone.current.fork({name: 'Constructor Zone2'}); + const constructorZone3: Zone = Zone.current.fork({name: 'Constructor Zone3'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return new Rx.Observable(subscriber => { + expect(Zone.current.name).toEqual(constructorZone1.name); + subscriber.next(1); + subscriber.next(2); + subscriber.complete(); + }); + }); + + observable2 = constructorZone2.run(() => { + return Rx.Observable.range(3, 4); + }); + + constructorZone3.run(() => { + concatObservable = Rx.Observable.concat(observable1, observable2, Rx.Scheduler.asap); + }); + + subscriptionZone.run(() => { + concatObservable.subscribe( + (concat: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(concat); + }, + (error: any) => { + fail('subscribe failed' + error); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 2, 3, 4, 5, 6]); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.defer.spec.ts b/test/rxjs/rxjs.defer.spec.ts new file mode 100644 index 000000000..348f87117 --- /dev/null +++ b/test/rxjs/rxjs.defer.spec.ts @@ -0,0 +1,46 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as Rx from 'rxjs/Rx'; + +describe('Observable.defer', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('defer func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.defer(() => { + return new Rx.Observable(subscribe => { + log.push('setup'); + expect(Zone.current.name).toEqual(constructorZone1.name); + subscribe.next(1); + subscribe.complete(); + return () => { + expect(Zone.current.name).toEqual(constructorZone1.name); + log.push('cleanup'); + }; + }); + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe((result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }); + }); + + expect(log).toEqual(['setup', 1, 'cleanup']); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.empty.spec.ts b/test/rxjs/rxjs.empty.spec.ts new file mode 100644 index 000000000..4f4e2c8a4 --- /dev/null +++ b/test/rxjs/rxjs.empty.spec.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +describe('Observable.empty', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('empty func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.empty(); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + fail('should not call next'); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.forkjoin.spec.ts b/test/rxjs/rxjs.forkjoin.spec.ts new file mode 100644 index 000000000..a02a79fde --- /dev/null +++ b/test/rxjs/rxjs.forkjoin.spec.ts @@ -0,0 +1,70 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as Rx from 'rxjs/Rx'; + +describe('Observable.forkjoin', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('forkjoin func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.forkJoin(Rx.Observable.range(1, 2), Rx.Observable.from([4, 5])); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + expect(log).toEqual([[2, 5], 'completed']); + }); + + it('forkjoin func callback with selector should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.forkJoin( + Rx.Observable.range(1, 2), Rx.Observable.from([4, 5]), function(x, y) { + expect(Zone.current.name).toEqual(constructorZone1.name); + return x + y; + }); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + expect(log).toEqual([7, 'completed']); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.from.spec.ts b/test/rxjs/rxjs.from.spec.ts new file mode 100644 index 000000000..027f4af54 --- /dev/null +++ b/test/rxjs/rxjs.from.spec.ts @@ -0,0 +1,95 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.from', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('from array should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.from([1, 2]); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + expect(log).toEqual([1, 2, 'completed']); + }); + + it('from array like object should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.from('foo'); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + expect(log).toEqual(['f', 'o', 'o', 'completed']); + }); + + it('from promise object should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.from(new Promise((resolve, reject) => { + resolve(1); + })); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + (error: any) => { + fail('should not call error' + error); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + expect(log).toEqual([1, 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.fromEvent.spec.ts b/test/rxjs/rxjs.fromEvent.spec.ts new file mode 100644 index 000000000..cbc2ba768 --- /dev/null +++ b/test/rxjs/rxjs.fromEvent.spec.ts @@ -0,0 +1,106 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as Rx from 'rxjs/Rx'; + +import {isBrowser} from '../../lib/common/utils'; +import {ifEnvSupports} from '../test-util'; + +function isEventTarget() { + return isBrowser; +} + +(isEventTarget as any).message = 'EventTargetTest'; + +describe('Observable.fromEvent', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const triggerZone: Zone = Zone.current.fork({name: 'Trigger Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('fromEvent EventTarget func callback should run in the correct zone', + ifEnvSupports(isEventTarget, () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.fromEvent(document, 'click'); + }); + + const clickEvent = document.createEvent('Event'); + clickEvent.initEvent('click', true, true); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + triggerZone.run(() => { + document.dispatchEvent(clickEvent); + }); + + expect(log).toEqual([clickEvent]); + })); + + it('fromEventPattern EventTarget func callback should run in the correct zone', + ifEnvSupports(isEventTarget, () => { + observable1 = constructorZone1.run(() => { + const handler = function() { + log.push('handler'); + }; + return Rx.Observable.fromEventPattern( + () => { + expect(Zone.current.name).toEqual(constructorZone1.name); + document.addEventListener('click', handler); + log.push('addListener'); + }, + () => { + expect(Zone.current.name).toEqual(constructorZone1.name); + document.removeEventListener('click', handler); + log.push('removeListener'); + }); + }); + + const clickEvent = document.createEvent('Event'); + clickEvent.initEvent('click', true, true); + + const subscriper: any = subscriptionZone.run(() => { + return observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + triggerZone.run(() => { + document.dispatchEvent(clickEvent); + subscriper.complete(); + }); + + expect(log).toEqual(['addListener', clickEvent, 'handler', 'completed', 'removeListener']); + })); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.fromPromise.spec.ts b/test/rxjs/rxjs.fromPromise.spec.ts new file mode 100644 index 000000000..eba29e643 --- /dev/null +++ b/test/rxjs/rxjs.fromPromise.spec.ts @@ -0,0 +1,51 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.fromPromise', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('fromPromise func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const promiseZone1: Zone = Zone.current.fork({name: 'Promise Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let res: any; + let promise: any = promiseZone1.run(() => { + return new Promise((resolve, reject) => { + res = resolve; + }); + }); + observable1 = constructorZone1.run(() => { + return Rx.Observable.fromPromise(promise); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + expect(log).toEqual([1]); + done(); + }, + () => { + fail('should not call error'); + }, + () => {}); + }); + res(1); + + expect(log).toEqual([]); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.interval.spec.ts b/test/rxjs/rxjs.interval.spec.ts new file mode 100644 index 000000000..39bd5244f --- /dev/null +++ b/test/rxjs/rxjs.interval.spec.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.interval', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('interval func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.interval(10); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + if (result >= 3) { + subscriber.complete(); + expect(log).toEqual([0, 1, 2, 3]); + done(); + } + }, + () => { + fail('should not call error'); + }, + () => {}); + }); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.merge.spec.ts b/test/rxjs/rxjs.merge.spec.ts new file mode 100644 index 000000000..144528d77 --- /dev/null +++ b/test/rxjs/rxjs.merge.spec.ts @@ -0,0 +1,52 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.merge', () => { + let log: string[]; + + beforeEach(() => { + log = []; + }); + + it('merge func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const constructorZone2: Zone = Zone.current.fork({name: 'Constructor Zone2'}); + const constructorZone3: Zone = Zone.current.fork({name: 'Constructor Zone3'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + const observable1: any = constructorZone1.run(() => { + return Rx.Observable.interval(8).map(v => 'observable1' + v).take(1); + }); + + const observable2: any = constructorZone2.run(() => { + return Rx.Observable.interval(10).map(v => 'observable2' + v).take(1); + }); + + const observable3: any = constructorZone3.run(() => { + return Rx.Observable.merge(observable1, observable2); + }); + + subscriptionZone.run(() => { + const subscriber = observable3.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual(['observable10', 'observable20', 'completed']); + done(); + }); + }); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.never.spec.ts b/test/rxjs/rxjs.never.spec.ts new file mode 100644 index 000000000..e4c6721b9 --- /dev/null +++ b/test/rxjs/rxjs.never.spec.ts @@ -0,0 +1,41 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +describe('Observable.never', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('never func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.never().startWith(7); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + fail('should not call complete'); + }); + }); + + expect(log).toEqual([7]); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.of.spec.ts b/test/rxjs/rxjs.of.spec.ts new file mode 100644 index 000000000..929036ac5 --- /dev/null +++ b/test/rxjs/rxjs.of.spec.ts @@ -0,0 +1,42 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +describe('Observable.of', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('of func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.of(1, 2, 3); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push(result); + }, + () => { + fail('should not call error'); + }, + () => { + expect(Zone.current.name).toEqual(subscriptionZone.name); + log.push('completed'); + }); + }); + + expect(log).toEqual([1, 2, 3, 'completed']); + }); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.range.spec.ts b/test/rxjs/rxjs.range.spec.ts new file mode 100644 index 000000000..0aa826f7a --- /dev/null +++ b/test/rxjs/rxjs.range.spec.ts @@ -0,0 +1,70 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.range', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('range func callback should run in the correct zone', () => { + observable1 = constructorZone1.run(() => { + return Rx.Observable.range(1, 3); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + + expect(log).toEqual([1, 2, 3, 'completed']); + }); + + it('range func callback should run in the correct zone with scheduler', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.range(1, 3, Rx.Scheduler.asap); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([1, 2, 3, 'completed']); + done(); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.spec.ts b/test/rxjs/rxjs.spec.ts index 0b93d66b2..0515a3ffd 100644 --- a/test/rxjs/rxjs.spec.ts +++ b/test/rxjs/rxjs.spec.ts @@ -5,7 +5,45 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import '../../lib/rxjs/rxjs'; import './rxjs.common.spec'; import './rxjs.bindCallback.spec'; import './rxjs.bindNodeCallback.spec'; -import './rxjs.combineLatest.spec'; \ No newline at end of file +import './rxjs.combineLatest.spec'; +import './rxjs.concat.spec'; +import './rxjs.defer.spec'; +import './rxjs.empty.spec'; +import './rxjs.forkjoin.spec'; +import './rxjs.from.spec'; +import './rxjs.fromEvent.spec'; +import './rxjs.fromPromise.spec'; +import './rxjs.interval.spec'; +import './rxjs.merge.spec'; +import './rxjs.never.spec'; +import './rxjs.of.spec'; +import './rxjs.range.spec'; +import './rxjs.throw.spec'; +import './rxjs.timer.spec'; +import './rxjs.zip.spec'; +import './rxjs.Observable.audit.spec'; +import './rxjs.Observable.buffer.spec'; +import './rxjs.Observable.catch.spec'; +import './rxjs.Observable.combine.spec'; +import './rxjs.Observable.concat.spec'; +import './rxjs.Observable.count.spec'; +import './rxjs.Observable.debounce.spec'; +import './rxjs.Observable.default.spec'; +import './rxjs.Observable.delay.spec'; +import './rxjs.Observable.notification.spec'; +import './rxjs.Observable.distinct.spec'; +import './rxjs.Observable.do.spec'; +import './rxjs.Observable.collection.spec'; +// TODO: @JiaLiPassion, add exhaust test +import './rxjs.Observable.merge.spec'; +import './rxjs.Observable.multicast.spec'; +import './rxjs.Observable.map.spec'; +import './rxjs.Observable.race.spec'; +import './rxjs.Observable.sample.spec'; +import './rxjs.Observable.take.spec'; +import './rxjs.Observable.timeout.spec'; +import './rxjs.Observable.window.spec'; diff --git a/test/rxjs/rxjs.throw.spec.ts b/test/rxjs/rxjs.throw.spec.ts new file mode 100644 index 000000000..28555c358 --- /dev/null +++ b/test/rxjs/rxjs.throw.spec.ts @@ -0,0 +1,70 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.throw', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('throw func callback should run in the correct zone', () => { + let error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.throw(error); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + fail('should not call next'); + }, + (error: any) => { + log.push(error); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call complete'); + }); + }); + + expect(log).toEqual([error]); + }); + + it('throw func callback should run in the correct zone with scheduler', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + let error = new Error('test'); + observable1 = constructorZone1.run(() => { + return Rx.Observable.throw(error, Rx.Scheduler.asap); + }); + + subscriptionZone.run(() => { + observable1.subscribe( + (result: any) => { + fail('should not call next'); + }, + (error: any) => { + log.push(error); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([error]); + done(); + }, + () => { + fail('should not call complete'); + }); + }); + + expect(log).toEqual([]); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.timer.spec.ts b/test/rxjs/rxjs.timer.spec.ts new file mode 100644 index 000000000..45d4b1b66 --- /dev/null +++ b/test/rxjs/rxjs.timer.spec.ts @@ -0,0 +1,47 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; +import {asyncTest} from '../test-util'; + +describe('Observable.timer', () => { + let log: string[]; + let observable1: any; + + beforeEach(() => { + log = []; + }); + + it('timer func callback should run in the correct zone', asyncTest((done: any) => { + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + observable1 = constructorZone1.run(() => { + return Rx.Observable.timer(10, 20); + }); + + subscriptionZone.run(() => { + const subscriber = observable1.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + if (result >= 3) { + subscriber.complete(); + } + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + expect(log).toEqual([0, 1, 2, 3, 'completed']); + done(); + }); + }); + expect(log).toEqual([]); + }, Zone.root)); +}); \ No newline at end of file diff --git a/test/rxjs/rxjs.zip.spec.ts b/test/rxjs/rxjs.zip.spec.ts new file mode 100644 index 000000000..20ecd0f07 --- /dev/null +++ b/test/rxjs/rxjs.zip.spec.ts @@ -0,0 +1,51 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import * as Rx from 'rxjs/Rx'; + +describe('Observable.zip', () => { + let log: string[]; + const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'}); + const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'}); + + beforeEach(() => { + log = []; + }); + + it('zip func callback should run in the correct zone', () => { + const observable1: any = constructorZone1.run(() => { + return Rx.Observable.range(1, 3); + }); + const observable2: any = constructorZone1.run(() => { + return Rx.Observable.of('foo', 'bar', 'beer'); + }); + + const observable3: any = constructorZone1.run(() => { + return Rx.Observable.zip(observable1, observable2, function(n: number, str: string) { + expect(Zone.current.name).toEqual(constructorZone1.name); + return {n: n, str: str}; + }); + }); + + subscriptionZone.run(() => { + observable3.subscribe( + (result: any) => { + log.push(result); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }, + () => { + fail('should not call error'); + }, + () => { + log.push('completed'); + expect(Zone.current.name).toEqual(subscriptionZone.name); + }); + }); + + expect(log).toEqual([{n: 1, str: 'foo'}, {n: 2, str: 'bar'}, {n: 3, str: 'beer'}, 'completed']); + }); +}); \ No newline at end of file diff --git a/test/test-util.ts b/test/test-util.ts index 86781bc83..6f6e2cb38 100644 --- a/test/test-util.ts +++ b/test/test-util.ts @@ -90,7 +90,7 @@ export function isSupportSetErrorStack() { export function asyncTest(testFn: Function, zone: Zone = Zone.current) { const AsyncTestZoneSpec = (Zone as any)['AsyncTestZoneSpec']; return (done: Function) => { - let asyncTestZone: Zone = zone.fork(new AsyncTestZoneSpec(done, (error: Error) => { + let asyncTestZone: Zone = zone.fork(new AsyncTestZoneSpec(() => {}, (error: Error) => { fail(error); }, 'asyncTest')); asyncTestZone.run(testFn, this, [done]);