Skip to content

Commit c2b4d92

Browse files
JiaLiPassionAndrewKushnir
authored andcommitted
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
1 parent a87951a commit c2b4d92

2 files changed

Lines changed: 37 additions & 0 deletions

File tree

packages/zone.js/lib/jasmine/jasmine.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,33 @@ Zone.__load_patch('jasmine', (global: any, Zone: ZoneType, api: _ZonePrivate) =>
152152
return clock;
153153
};
154154
}
155+
156+
// monkey patch createSpyObj to make properties enumerable to true
157+
if (!(jasmine as any)[Zone.__symbol__('createSpyObj')]) {
158+
const originalCreateSpyObj = jasmine.createSpyObj;
159+
(jasmine as any)[Zone.__symbol__('createSpyObj')] = originalCreateSpyObj;
160+
jasmine.createSpyObj = function() {
161+
const args: any = Array.prototype.slice.call(arguments);
162+
const propertyNames = args.length >= 3 ? args[2] : null;
163+
let spyObj: any;
164+
if (propertyNames) {
165+
const defineProperty = Object.defineProperty;
166+
Object.defineProperty = function(obj: any, p: string, attributes: any) {
167+
return defineProperty.call(
168+
this, obj, p, {...attributes, configurable: true, enumerable: true});
169+
};
170+
try {
171+
spyObj = originalCreateSpyObj.apply(this, args);
172+
} finally {
173+
Object.defineProperty = defineProperty;
174+
}
175+
} else {
176+
spyObj = originalCreateSpyObj.apply(this, args);
177+
}
178+
return spyObj;
179+
};
180+
}
181+
155182
/**
156183
* Gets a function wrapping the body of a Jasmine `describe` block to execute in a
157184
* synchronous-only zone.

packages/zone.js/test/jasmine-patch.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,14 @@ ifEnvSupports(supportJasmineSpec, () => {
7777
expect(log).toEqual(['resolved']);
7878
});
7979
});
80+
81+
describe('jasmine.createSpyObj', () => {
82+
it('createSpyObj with properties should be able to be retrieved from the spy', () => {
83+
const spy = jasmine.createSpyObj('obj', ['someFunction'], {prop1: 'foo'});
84+
expect(spy.prop1).toEqual('foo');
85+
const desc: any = Object.getOwnPropertyDescriptor(spy, 'prop1');
86+
expect(desc.enumerable).toBe(true);
87+
expect(desc.configurable).toBe(true);
88+
});
89+
});
8090
})();

0 commit comments

Comments
 (0)