feat(zone.js): patch jasmine.createSpyObj to make properties enumerable to be true (#34624)
Close #33657 in jasmine 3.5, there is a new feature, user can pass a properties object to `jasmine.createSpyObj` ``` const spy = jasmine.createSpyObj('spy', ['method1'], {prop1: 'foo'}); expect(spy.prop1).toEqual('foo'); ``` This case will not work for Angular TestBed, for example, ``` describe('AppComponent', () => { beforeEach(() => { //Note the third parameter // @ts-ignore const someServiceSpy = jasmine.createSpyObj('SomeService', ['someFunction'], ['aProperty']); TestBed.configureTestingModule({ declarations: [ AppComponent ], providers: [ {provide: SomeService, useValue: someServiceSpy}, ] }).compileComponents(); }); it('should create the app', () => { //spyObj will have someFunction, but will not have aProperty let spyObj = TestBed.get(SomeService); }); ``` Because `jasmine.createSpyObj` will create the `aProperty` with `enumerable=false`, and `TestBed.configureTestingModule` will try to copy all the properties from spyObj to the injected service instance. And because `enumerable` is false, so the property (here is aProperty) will not be copied. This PR will monkey patch the `jasmine.createSpyObj` and make sure the new property's `enumerable=true`. PR Close #34624
This commit is contained in:
		
							parent
							
								
									a87951a28f
								
							
						
					
					
						commit
						c2b4d92708
					
				| @ -152,6 +152,33 @@ Zone.__load_patch('jasmine', (global: any, Zone: ZoneType, api: _ZonePrivate) => | ||||
|       return clock; | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   // monkey patch createSpyObj to make properties enumerable to true
 | ||||
|   if (!(jasmine as any)[Zone.__symbol__('createSpyObj')]) { | ||||
|     const originalCreateSpyObj = jasmine.createSpyObj; | ||||
|     (jasmine as any)[Zone.__symbol__('createSpyObj')] = originalCreateSpyObj; | ||||
|     jasmine.createSpyObj = function() { | ||||
|       const args: any = Array.prototype.slice.call(arguments); | ||||
|       const propertyNames = args.length >= 3 ? args[2] : null; | ||||
|       let spyObj: any; | ||||
|       if (propertyNames) { | ||||
|         const defineProperty = Object.defineProperty; | ||||
|         Object.defineProperty = function(obj: any, p: string, attributes: any) { | ||||
|           return defineProperty.call( | ||||
|               this, obj, p, {...attributes, configurable: true, enumerable: true}); | ||||
|         }; | ||||
|         try { | ||||
|           spyObj = originalCreateSpyObj.apply(this, args); | ||||
|         } finally { | ||||
|           Object.defineProperty = defineProperty; | ||||
|         } | ||||
|       } else { | ||||
|         spyObj = originalCreateSpyObj.apply(this, args); | ||||
|       } | ||||
|       return spyObj; | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Gets a function wrapping the body of a Jasmine `describe` block to execute in a | ||||
|    * synchronous-only zone. | ||||
|  | ||||
| @ -77,4 +77,14 @@ ifEnvSupports(supportJasmineSpec, () => { | ||||
|       expect(log).toEqual(['resolved']); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('jasmine.createSpyObj', () => { | ||||
|     it('createSpyObj with properties should be able to be retrieved from the spy', () => { | ||||
|       const spy = jasmine.createSpyObj('obj', ['someFunction'], {prop1: 'foo'}); | ||||
|       expect(spy.prop1).toEqual('foo'); | ||||
|       const desc: any = Object.getOwnPropertyDescriptor(spy, 'prop1'); | ||||
|       expect(desc.enumerable).toBe(true); | ||||
|       expect(desc.configurable).toBe(true); | ||||
|     }); | ||||
|   }); | ||||
| })(); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user