From c8be987b40a645534f87a2fc2c33e4e30d3fef28 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 23 Sep 2019 16:15:11 -0700 Subject: [PATCH] fix(ivy): ensure TestBed restores fields to the most original value (#32823) PR Close #32823 --- packages/core/test/test_bed_spec.ts | 32 ++++++++++++++++++- .../core/testing/src/r3_test_bed_compiler.ts | 6 ++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/core/test/test_bed_spec.ts b/packages/core/test/test_bed_spec.ts index 7d5786cc4d..da6022635f 100644 --- a/packages/core/test/test_bed_spec.ts +++ b/packages/core/test/test_bed_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Directive, ErrorHandler, Inject, Injectable, InjectionToken, Input, ModuleWithProviders, NgModule, Optional, Pipe, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineNgModule as defineNgModule, ɵɵtext as text} from '@angular/core'; +import {Compiler, Component, Directive, ErrorHandler, Inject, Injectable, InjectionToken, Input, ModuleWithProviders, NgModule, Optional, Pipe, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineNgModule as defineNgModule, ɵɵtext as text} from '@angular/core'; import {TestBed, getTestBed} from '@angular/core/testing/src/test_bed'; import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -699,5 +699,35 @@ describe('TestBed', () => { // no trace of the overridden providers. expect((Module as any).ngInjectorDef.providers).toEqual([Token]); }); + + it('should clean up overridden providers on components whose modules are compiled more than once', + async() => { + @Injectable() + class SomeInjectable { + id: string|undefined; + } + + @Component({providers: [SomeInjectable]}) + class ComponentWithProvider { + constructor(readonly injectable: SomeInjectable) {} + } + + @NgModule({declarations: [ComponentWithProvider]}) + class MyModule { + } + + TestBed.configureTestingModule({imports: [MyModule]}); + const originalResolver = + (ComponentWithProvider as any).ngComponentDef.providersResolver; + TestBed.overrideProvider(SomeInjectable, {useValue: {id: 'fake'}}); + + const compiler = TestBed.inject(Compiler); + await compiler.compileModuleAsync(MyModule); + compiler.compileModuleSync(MyModule); + + TestBed.resetTestingModule(); + expect((ComponentWithProvider as any).ngComponentDef.providersResolver) + .toEqual(originalResolver); + }); }); }); diff --git a/packages/core/testing/src/r3_test_bed_compiler.ts b/packages/core/testing/src/r3_test_bed_compiler.ts index a0e2eeeb63..23916ec504 100644 --- a/packages/core/testing/src/r3_test_bed_compiler.ts +++ b/packages/core/testing/src/r3_test_bed_compiler.ts @@ -513,9 +513,9 @@ export class R3TestBedCompiler { } restoreOriginalState(): void { - for (const op of this.defCleanupOps) { - op.def[op.field] = op.original; - } + // Process cleanup ops in reverse order so the field's original value is restored correctly (in + // case there were multiple overrides for the same field). + forEachRight(this.defCleanupOps, (op: CleanupOperation) => { op.def[op.field] = op.original; }); // Restore initial component/directive/pipe defs this.initialNgDefs.forEach( (value: [string, PropertyDescriptor | undefined], type: Type) => {