fix(ivy): TestBed not unwrapping imports array function when overriding provider (#34629)
Fixes an error that is thrown when a provider is overridden in `TestBed`, if the module definition of one of the imported modules uses a function for the `imports` that is set via `setNgModuleScope`. The problem was that we have a `for...of` loop that assumes that the imports are an array, but they can also be a function. This was handled correctly in other places, but this one was missed. Note that the above-mentioned error is only thrown at runtime when the code is transpiled to es6. In es5 TS generates a call to a helper that handles the error silently so the attached unit test only fails in es6. Fixes #34623. PR Close #34629
This commit is contained in:
parent
ee46b9b44f
commit
5b864ede13
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* 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 {TestBed, getTestBed} from '@angular/core/testing/src/test_bed';
|
||||||
import {By} from '@angular/platform-browser';
|
import {By} from '@angular/platform-browser';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
@ -1002,4 +1002,44 @@ describe('TestBed', () => {
|
||||||
expect(TestBed.inject(Service)).toBeDefined();
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -413,8 +413,9 @@ export class R3TestBedCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply provider overrides to imported modules recursively
|
// Apply provider overrides to imported modules recursively
|
||||||
const moduleDef: any = (moduleType as any)[NG_MOD_DEF];
|
const moduleDef = (moduleType as any)[NG_MOD_DEF];
|
||||||
for (const importedModule of moduleDef.imports) {
|
const imports = maybeUnwrapFn(moduleDef.imports);
|
||||||
|
for (const importedModule of imports) {
|
||||||
this.applyProviderOverridesToModule(importedModule);
|
this.applyProviderOverridesToModule(importedModule);
|
||||||
}
|
}
|
||||||
// Also override the providers on any ModuleWithProviders imports since those don't appear in
|
// Also override the providers on any ModuleWithProviders imports since those don't appear in
|
||||||
|
|
Loading…
Reference in New Issue