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']);
 | |
|   });
 | |
| });
 |