fix(ivy): avoid using stale cache in TestBed if module overrides are defined (#33787)

NgModule compilation in JIT mode (that is also used in TestBed) caches module scopes on NgModule defs (using `transitiveCompileScopes` field). Module overrides (defined via TestBed.overrideModule) may invalidate this data by adding/removing items in `declarations` list. This commit forces TestBed to recalculate transitive scopes in case module overrides are present, so TestBed always gets the most up-to-date information.

PR Close #33787
This commit is contained in:
Andrew Kushnir
2019-11-12 23:03:46 -08:00
committed by Alex Rickabaugh
parent a09d60cb50
commit fbd2133610
3 changed files with 83 additions and 16 deletions

View File

@ -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, ɵ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, ViewChild, ɵ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';
@ -289,6 +289,59 @@ describe('TestBed', () => {
expect(SimpleService.ngOnDestroyCalls).toBe(0);
});
describe('module overrides using TestBed.overrideModule', () => {
@Component({
selector: 'test-cmp',
template: '...',
})
class TestComponent {
testField = 'default';
}
@NgModule({
declarations: [TestComponent],
exports: [TestComponent],
})
class TestModule {
}
@Component({
selector: 'app-root',
template: `<test-cmp #testCmpCtrl></test-cmp>`,
})
class AppComponent {
@ViewChild('testCmpCtrl', {static: true}) testCmpCtrl !: TestComponent;
}
@NgModule({
declarations: [AppComponent],
imports: [TestModule],
})
class AppModule {
}
@Component({
selector: 'test-cmp',
template: '...',
})
class MockTestComponent {
testField = 'overwritten';
}
it('should allow declarations override', () => {
TestBed.configureTestingModule({
imports: [AppModule],
});
// replace TestComponent with MockTestComponent
TestBed.overrideModule(TestModule, {
remove: {declarations: [TestComponent], exports: [TestComponent]},
add: {declarations: [MockTestComponent], exports: [MockTestComponent]}
});
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.testCmpCtrl.testField).toBe('overwritten');
});
});
describe('multi providers', () => {
const multiToken = new InjectionToken<string[]>('multiToken');
const singleToken = new InjectionToken<string>('singleToken');