From 87fe47737aaab9a75c0d0e5bf14d46f41a72bdff Mon Sep 17 00:00:00 2001 From: Julie Ralph Date: Fri, 12 Aug 2016 17:39:33 -0700 Subject: [PATCH] fix(testing): override metadata subclasses properly (#10767) This fixes an issue where `TestBed.overrideComponent(MyComp, {})` would remove some properties including `providers` from the component. This was due to the override not properly dealing with getter fields on subclasses. --- .../compiler/test/metadata_overrider_spec.ts | 36 +++++++++++++++++++ .../compiler/testing/metadata_overrider.ts | 17 +++++---- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/modules/@angular/compiler/test/metadata_overrider_spec.ts b/modules/@angular/compiler/test/metadata_overrider_spec.ts index 501329d173..4d6ca8ca28 100644 --- a/modules/@angular/compiler/test/metadata_overrider_spec.ts +++ b/modules/@angular/compiler/test/metadata_overrider_spec.ts @@ -15,6 +15,10 @@ interface SomeMetadataType { arrayProp?: any[]; } +interface OtherMetadataType extends SomeMetadataType { + otherPlainProp?: string; +} + class SomeMetadata implements SomeMetadataType { plainProp: string; private _getterProp: string; @@ -28,6 +32,20 @@ class SomeMetadata implements SomeMetadataType { } } +class OtherMetadata extends SomeMetadata implements OtherMetadataType { + otherPlainProp: string; + + constructor(options: OtherMetadataType) { + super({ + plainProp: options.plainProp, + getterProp: options.getterProp, + arrayProp: options.arrayProp + }); + + this.otherPlainProp = options.otherPlainProp; + } +} + export function main() { describe('metadata overrider', () => { let overrider: MetadataOverrider; @@ -112,5 +130,23 @@ export function main() { }); }); + + describe('subclasses', () => { + it('should set individual properties and keep others', () => { + const oldInstance = new OtherMetadata({ + plainProp: 'somePlainProp', + getterProp: 'someGetterProp', + otherPlainProp: 'newOtherProp' + }); + const newInstance = overrider.overrideMetadata( + OtherMetadata, oldInstance, {set: {plainProp: 'newPlainProp'}}); + expect(newInstance).toEqual(new OtherMetadata({ + plainProp: 'newPlainProp', + getterProp: 'someGetterProp', + otherPlainProp: 'newOtherProp' + })); + }); + + }); }); } diff --git a/modules/@angular/compiler/testing/metadata_overrider.ts b/modules/@angular/compiler/testing/metadata_overrider.ts index defab71922..ec96f88c21 100644 --- a/modules/@angular/compiler/testing/metadata_overrider.ts +++ b/modules/@angular/compiler/testing/metadata_overrider.ts @@ -118,13 +118,16 @@ function _valueProps(obj: any): string[] { props.push(prop); } }); + // getters - const proto = Object.getPrototypeOf(obj); - Object.keys(proto).forEach((protoProp) => { - var desc = Object.getOwnPropertyDescriptor(proto, protoProp); - if (!protoProp.startsWith('_') && desc && 'get' in desc) { - props.push(protoProp); - } - }); + let proto = obj; + while (proto = Object.getPrototypeOf(proto)) { + Object.keys(proto).forEach((protoProp) => { + var desc = Object.getOwnPropertyDescriptor(proto, protoProp); + if (!protoProp.startsWith('_') && desc && 'get' in desc) { + props.push(protoProp); + } + }); + } return props; }