diff --git a/packages/core/test/test_bed_spec.ts b/packages/core/test/test_bed_spec.ts index 7c4ab3bf25..2c28381644 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 {Compiler, Component, Directive, ErrorHandler, Inject, Injectable, InjectionToken, Injector, Input, ModuleWithProviders, NgModule, Optional, Pipe, Type, ViewChild, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineNgModule as defineNgModule, ɵɵtext as text} from '@angular/core'; +import {Compiler, Component, Directive, ErrorHandler, Inject, Injectable, InjectionToken, Injector, Input, ModuleWithProviders, NgModule, Optional, Pipe, Type, ViewChild, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵsetNgModuleScope as setNgModuleScope, ɵɵ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'; @@ -1002,4 +1002,44 @@ describe('TestBed', () => { expect(TestBed.inject(Service)).toBeDefined(); }); }); + + onlyInIvy('uses Ivy-specific compiler output') + .it('should handle provider overrides when module imports are provided as a function', () => { + class InjectedString { + value?: string; + } + + @Component({template: '{{injectedString.value}}'}) + class AppComponent { + constructor(public injectedString: InjectedString) {} + } + + @NgModule({}) + class DependencyModule { + } + + // We need to write the compiler output manually here, + // because it depends on code generated by ngcc. + class TestingModule { + static ɵmod = defineNgModule({type: TestingModule}); + static ɵinj = + defineInjector({factory: () => new TestingModule(), imports: [DependencyModule]}); + } + setNgModuleScope(TestingModule, {imports: () => [DependencyModule]}); + + TestBed + .configureTestingModule({ + imports: [TestingModule], + declarations: [AppComponent], + providers: [{provide: InjectedString, useValue: {value: 'initial'}}], + }) + .compileComponents(); + + TestBed.overrideProvider(InjectedString, {useValue: {value: 'changed'}}) + .compileComponents(); + + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + expect(fixture !.nativeElement.textContent).toContain('changed'); + }); }); diff --git a/packages/core/testing/src/r3_test_bed_compiler.ts b/packages/core/testing/src/r3_test_bed_compiler.ts index f4296ed733..f64995cf71 100644 --- a/packages/core/testing/src/r3_test_bed_compiler.ts +++ b/packages/core/testing/src/r3_test_bed_compiler.ts @@ -413,8 +413,9 @@ export class R3TestBedCompiler { } // Apply provider overrides to imported modules recursively - const moduleDef: any = (moduleType as any)[NG_MOD_DEF]; - for (const importedModule of moduleDef.imports) { + const moduleDef = (moduleType as any)[NG_MOD_DEF]; + const imports = maybeUnwrapFn(moduleDef.imports); + for (const importedModule of imports) { this.applyProviderOverridesToModule(importedModule); } // Also override the providers on any ModuleWithProviders imports since those don't appear in