210 lines
7.8 KiB
TypeScript
210 lines
7.8 KiB
TypeScript
|
/**
|
||
|
* @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 {Observable, Subject} from 'rxjs';
|
||
|
import {map} from 'rxjs/operators';
|
||
|
|
||
|
/**
|
||
|
* The point of these tests, is to ensure that all callbacks execute in the Zone which was active
|
||
|
* when the callback was passed into the Rx.
|
||
|
*
|
||
|
* The implications are:
|
||
|
* - Observable callback passed into `Observable` executes in the same Zone as when the
|
||
|
* `new Observable` was invoked.
|
||
|
* - The subscription callbacks passed into `subscribe` execute in the same Zone as when the
|
||
|
* `subscribe` method was invoked.
|
||
|
* - The operator callbacks passe into `map`, etc..., execute in the same Zone as when the
|
||
|
* `operator` (`lift`) method was invoked.
|
||
|
*/
|
||
|
describe('Zone interaction', () => {
|
||
|
it('should run methods in the zone of declaration', () => {
|
||
|
const log: any[] = [];
|
||
|
const constructorZone: Zone = Zone.current.fork({name: 'Constructor Zone'});
|
||
|
const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'});
|
||
|
let subscriber: any = null;
|
||
|
const observable: any =
|
||
|
constructorZone.run(() => new Observable((_subscriber: any) => {
|
||
|
subscriber = _subscriber;
|
||
|
log.push('setup');
|
||
|
expect(Zone.current.name).toEqual(constructorZone.name);
|
||
|
return () => {
|
||
|
expect(Zone.current.name).toEqual(constructorZone.name);
|
||
|
log.push('cleanup');
|
||
|
};
|
||
|
}));
|
||
|
subscriptionZone.run(
|
||
|
() => observable.subscribe(
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone.name);
|
||
|
log.push('next');
|
||
|
},
|
||
|
(): any => null,
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone.name);
|
||
|
log.push('complete');
|
||
|
}));
|
||
|
subscriber.next('MyValue');
|
||
|
subscriber.complete();
|
||
|
|
||
|
expect(log).toEqual(['setup', 'next', 'complete', 'cleanup']);
|
||
|
log.length = 0;
|
||
|
|
||
|
subscriptionZone.run(() => observable.subscribe((): any => null, () => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone.name);
|
||
|
log.push('error');
|
||
|
}, (): any => null));
|
||
|
subscriber.next('MyValue');
|
||
|
subscriber.error('MyError');
|
||
|
|
||
|
expect(log).toEqual(['setup', 'error', 'cleanup']);
|
||
|
});
|
||
|
|
||
|
it('should run methods in the zone of declaration when nexting synchronously', () => {
|
||
|
const log: any[] = [];
|
||
|
const rootZone: Zone = Zone.current;
|
||
|
const constructorZone: Zone = Zone.current.fork({name: 'Constructor Zone'});
|
||
|
const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'});
|
||
|
const observable: any =
|
||
|
constructorZone.run(() => new Observable((subscriber: any) => {
|
||
|
// Execute the `next`/`complete` in different zone, and assert that
|
||
|
// correct zone
|
||
|
// is restored.
|
||
|
rootZone.run(() => {
|
||
|
subscriber.next('MyValue');
|
||
|
subscriber.complete();
|
||
|
});
|
||
|
return () => {
|
||
|
expect(Zone.current.name).toEqual(constructorZone.name);
|
||
|
log.push('cleanup');
|
||
|
};
|
||
|
}));
|
||
|
|
||
|
subscriptionZone.run(
|
||
|
() => observable.subscribe(
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone.name);
|
||
|
log.push('next');
|
||
|
},
|
||
|
(): any => null,
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone.name);
|
||
|
log.push('complete');
|
||
|
}));
|
||
|
|
||
|
expect(log).toEqual(['next', 'complete', 'cleanup']);
|
||
|
});
|
||
|
|
||
|
it('should run operators in the zone of declaration', () => {
|
||
|
const log: any[] = [];
|
||
|
const rootZone: Zone = Zone.current;
|
||
|
const constructorZone: Zone = Zone.current.fork({name: 'Constructor Zone'});
|
||
|
const operatorZone: Zone = Zone.current.fork({name: 'Operator Zone'});
|
||
|
const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'});
|
||
|
let observable: any =
|
||
|
constructorZone.run(() => new Observable((subscriber: any) => {
|
||
|
// Execute the `next`/`complete` in different zone, and assert that
|
||
|
// correct zone
|
||
|
// is restored.
|
||
|
rootZone.run(() => {
|
||
|
subscriber.next('MyValue');
|
||
|
subscriber.complete();
|
||
|
});
|
||
|
return () => {
|
||
|
expect(Zone.current.name).toEqual(constructorZone.name);
|
||
|
log.push('cleanup');
|
||
|
};
|
||
|
}));
|
||
|
|
||
|
observable = operatorZone.run(() => observable.pipe(map((value: any) => {
|
||
|
expect(Zone.current.name).toEqual(operatorZone.name);
|
||
|
log.push('map: ' + value);
|
||
|
return value;
|
||
|
})));
|
||
|
|
||
|
subscriptionZone.run(
|
||
|
() => observable.subscribe(
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone.name);
|
||
|
log.push('next');
|
||
|
},
|
||
|
(e: any) => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone.name);
|
||
|
log.push('error: ' + e);
|
||
|
},
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone.name);
|
||
|
log.push('complete');
|
||
|
}));
|
||
|
|
||
|
expect(log).toEqual(['map: MyValue', 'next', 'complete', 'cleanup']);
|
||
|
});
|
||
|
|
||
|
it('should run subscribe in zone of declaration with Observable.create', () => {
|
||
|
const log: any[] = [];
|
||
|
const constructorZone: Zone = Zone.current.fork({name: 'Constructor Zone'});
|
||
|
let observable: any = constructorZone.run(() => Observable.create((subscriber: any) => {
|
||
|
expect(Zone.current.name).toEqual(constructorZone.name);
|
||
|
subscriber.next(1);
|
||
|
subscriber.complete();
|
||
|
return () => {
|
||
|
expect(Zone.current.name).toEqual(constructorZone.name);
|
||
|
log.push('cleanup');
|
||
|
};
|
||
|
}));
|
||
|
|
||
|
observable.subscribe(() => { log.push('next'); });
|
||
|
|
||
|
expect(log).toEqual(['next', 'cleanup']);
|
||
|
});
|
||
|
|
||
|
it('should run in the zone when subscribe is called to the same Subject', () => {
|
||
|
const log: any[] = [];
|
||
|
const constructorZone: Zone = Zone.current.fork({name: 'Constructor Zone'});
|
||
|
const subscriptionZone1: Zone = Zone.current.fork({name: 'Subscription Zone 1'});
|
||
|
const subscriptionZone2: Zone = Zone.current.fork({name: 'Subscription Zone 2'});
|
||
|
|
||
|
let subject: any;
|
||
|
|
||
|
constructorZone.run(() => { subject = new Subject(); });
|
||
|
|
||
|
let subscription1: any;
|
||
|
let subscription2: any;
|
||
|
|
||
|
subscriptionZone1.run(() => {
|
||
|
subscription1 = subject.subscribe(
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone1.name);
|
||
|
log.push('next1');
|
||
|
},
|
||
|
() => {},
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone1.name);
|
||
|
log.push('complete1');
|
||
|
});
|
||
|
});
|
||
|
|
||
|
subscriptionZone2.run(() => {
|
||
|
subscription2 = subject.subscribe(
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone2.name);
|
||
|
log.push('next2');
|
||
|
},
|
||
|
() => {},
|
||
|
() => {
|
||
|
expect(Zone.current.name).toEqual(subscriptionZone2.name);
|
||
|
log.push('complete2');
|
||
|
});
|
||
|
});
|
||
|
|
||
|
subject.next(1);
|
||
|
subject.complete();
|
||
|
|
||
|
expect(log).toEqual(['next1', 'next2', 'complete1', 'complete2']);
|
||
|
});
|
||
|
});
|