@@ -71,9 +71,10 @@ export class R3TestBedCompiler {
7171
7272 // Map that keeps initial version of component/directive/pipe defs in case
7373 // we compile a Type again, thus overriding respective static fields. This is
74- // required to make sure we restore defs to their initial states between test runs
75- // TODO: we should support the case with multiple defs on a type
76- private initialNgDefs = new Map < Type < any > , [ string , PropertyDescriptor | undefined ] > ( ) ;
74+ // required to make sure we restore defs to their initial states between test runs.
75+ // Note: one class may have multiple defs (for example: ɵmod and ɵinj in case of an
76+ // NgModule), store all of them in a map.
77+ private initialNgDefs = new Map < Type < any > , Map < string , PropertyDescriptor | undefined > > ( ) ;
7778
7879 // Array that keeps cleanup operations for initial versions of component/directive/pipe/module
7980 // defs in case TestBed makes changes to the originals.
@@ -623,10 +624,20 @@ export class R3TestBedCompiler {
623624 return affectedModules ;
624625 }
625626
627+ /**
628+ * Preserve an original def (such as ɵmod, ɵinj, etc) before applying an override.
629+ * Note: one class may have multiple defs (for example: ɵmod and ɵinj in case of
630+ * an NgModule). If there is a def in a set already, don't override it, since
631+ * an original one should be restored at the end of a test.
632+ */
626633 private maybeStoreNgDef ( prop : string , type : Type < any > ) {
627634 if ( ! this . initialNgDefs . has ( type ) ) {
635+ this . initialNgDefs . set ( type , new Map ( ) ) ;
636+ }
637+ const currentDefs = this . initialNgDefs . get ( type ) ! ;
638+ if ( ! currentDefs . has ( prop ) ) {
628639 const currentDef = Object . getOwnPropertyDescriptor ( type , prop ) ;
629- this . initialNgDefs . set ( type , [ prop , currentDef ] ) ;
640+ currentDefs . set ( prop , currentDef ) ;
630641 }
631642 }
632643
@@ -668,20 +679,22 @@ export class R3TestBedCompiler {
668679 op . object [ op . fieldName ] = op . originalValue ;
669680 } ) ;
670681 // Restore initial component/directive/pipe defs
671- this . initialNgDefs . forEach ( ( value : [ string , PropertyDescriptor | undefined ] , type : Type < any > ) => {
672- const [ prop , descriptor ] = value ;
673- if ( ! descriptor ) {
674- // Delete operations are generally undesirable since they have performance implications
675- // on objects they were applied to. In this particular case, situations where this code
676- // is invoked should be quite rare to cause any noticeable impact, since it's applied
677- // only to some test cases (for example when class with no annotations extends some
678- // @Component ) when we need to clear 'ɵcmp' field on a given class to restore
679- // its original state (before applying overrides and running tests).
680- delete ( type as any ) [ prop ] ;
681- } else {
682- Object . defineProperty ( type , prop , descriptor ) ;
683- }
684- } ) ;
682+ this . initialNgDefs . forEach (
683+ ( defs : Map < string , PropertyDescriptor | undefined > , type : Type < any > ) => {
684+ defs . forEach ( ( descriptor , prop ) => {
685+ if ( ! descriptor ) {
686+ // Delete operations are generally undesirable since they have performance
687+ // implications on objects they were applied to. In this particular case, situations
688+ // where this code is invoked should be quite rare to cause any noticeable impact,
689+ // since it's applied only to some test cases (for example when class with no
690+ // annotations extends some @Component ) when we need to clear 'ɵcmp' field on a given
691+ // class to restore its original state (before applying overrides and running tests).
692+ delete ( type as any ) [ prop ] ;
693+ } else {
694+ Object . defineProperty ( type , prop , descriptor ) ;
695+ }
696+ } ) ;
697+ } ) ;
685698 this . initialNgDefs . clear ( ) ;
686699 this . moduleProvidersOverridden . clear ( ) ;
687700 this . restoreComponentResolutionQueue ( ) ;
0 commit comments