diff --git a/packages/core/testing/src/r3_test_bed.ts b/packages/core/testing/src/r3_test_bed.ts index 309d27a1d7..ecec3ae112 100644 --- a/packages/core/testing/src/r3_test_bed.ts +++ b/packages/core/testing/src/r3_test_bed.ts @@ -124,7 +124,11 @@ export class TestBedRender3 implements Injector, TestBed { } overrideTemplateUsingTestingModule(component: Type, template: string): void { - throw new Error('Render3TestBed.overrideTemplateUsingTestingModule is not implemented yet'); + if (this._instantiated) { + throw new Error( + 'Cannot override template when the test module has already been instantiated'); + } + this._templateOverrides.set(component, template); } static overrideProvider(token: any, provider: { @@ -185,6 +189,7 @@ export class TestBedRender3 implements Injector, TestBed { private _providerOverrides: Provider[] = []; private _rootProviderOverrides: Provider[] = []; private _providerOverridesByToken: Map = new Map(); + private _templateOverrides: Map, string> = new Map(); // test module configuration private _providers: Provider[] = []; @@ -195,6 +200,7 @@ export class TestBedRender3 implements Injector, TestBed { private _activeFixtures: ComponentFixture[] = []; private _moduleRef: NgModuleRef = null !; + private _testModuleType: NgModuleType = null !; private _instantiated: boolean = false; @@ -241,6 +247,7 @@ export class TestBedRender3 implements Injector, TestBed { this._providerOverrides = []; this._rootProviderOverrides = []; this._providerOverridesByToken.clear(); + this._templateOverrides.clear(); // reset test module config this._providers = []; @@ -248,6 +255,7 @@ export class TestBedRender3 implements Injector, TestBed { this._imports = []; this._schemas = []; this._moduleRef = null !; + this._testModuleType = null !; this._instantiated = false; this._activeFixtures.forEach((fixture) => { @@ -402,11 +410,11 @@ export class TestBedRender3 implements Injector, TestBed { } const resolvers = this._getResolvers(); - const testModuleType = this._createTestModule(); - this._compileNgModule(testModuleType, resolvers); + this._testModuleType = this._createTestModule(); + this._compileNgModule(this._testModuleType, resolvers); const parentInjector = this.platform.injector; - this._moduleRef = new NgModuleRef(testModuleType, parentInjector); + this._moduleRef = new NgModuleRef(this._testModuleType, parentInjector); // ApplicationInitStatus.runInitializers() is marked @internal // to core. Cast it to any before accessing it. @@ -472,15 +480,20 @@ export class TestBedRender3 implements Injector, TestBed { return DynamicTestModule as NgModuleType; } - private _getMetaWithOverrides(meta: Component|Directive|NgModule) { + private _getMetaWithOverrides(meta: Component|Directive|NgModule, type?: Type) { + const overrides: {providers?: any[], template?: string} = {}; if (meta.providers && meta.providers.length) { - const overrides = + const providerOverrides = flatten(meta.providers, (provider: any) => this._getProviderOverrides(provider)); - if (overrides.length) { - return {...meta, providers: [...meta.providers, ...overrides]}; + if (providerOverrides.length) { + overrides.providers = [...meta.providers, ...providerOverrides]; } } - return meta; + const hasTemplateOverride = !!type && this._templateOverrides.has(type); + if (hasTemplateOverride) { + overrides.template = this._templateOverrides.get(type !); + } + return Object.keys(overrides).length ? {...meta, ...overrides} : meta; } private _compileNgModule(moduleType: NgModuleType, resolvers: Resolvers): void { @@ -501,7 +514,7 @@ export class TestBedRender3 implements Injector, TestBed { declarations.forEach(declaration => { const component = resolvers.component.resolve(declaration); if (component) { - const metadata = this._getMetaWithOverrides(component); + const metadata = this._getMetaWithOverrides(component, declaration); compileComponent(declaration, metadata); compiledComponents.push(declaration); return; @@ -523,8 +536,15 @@ export class TestBedRender3 implements Injector, TestBed { // Compile transitive modules, components, directives and pipes const transitiveScope = this._transitiveScopesFor(moduleType, resolvers); - compiledComponents.forEach( - cmp => patchComponentDefWithScope((cmp as any).ngComponentDef, transitiveScope)); + compiledComponents.forEach(cmp => { + const scope = this._templateOverrides.has(cmp) ? + // if we have template override via `TestBed.overrideTemplateUsingTestingModule` - + // define Component scope as TestingModule scope, instead of the scope of NgModule + // where this Component was declared + this._transitiveScopesFor(this._testModuleType, resolvers) : + transitiveScope; + patchComponentDefWithScope((cmp as any).ngComponentDef, scope); + }); } /** diff --git a/packages/platform-browser/test/testing_public_spec.ts b/packages/platform-browser/test/testing_public_spec.ts index b2943bce3d..f4c15da29a 100644 --- a/packages/platform-browser/test/testing_public_spec.ts +++ b/packages/platform-browser/test/testing_public_spec.ts @@ -727,61 +727,58 @@ class CompWithUrlTemplate { }); describe('overrideTemplateUsingTestingModule', () => { - fixmeIvy('FW-851: TestBed.overrideTemplateUsingTestingModule is not implemented') - .it('should compile the template in the context of the testing module', () => { - @Component({selector: 'comp', template: 'a'}) - class MyComponent { - prop = 'some prop'; - } + it('should compile the template in the context of the testing module', () => { + @Component({selector: 'comp', template: 'a'}) + class MyComponent { + prop = 'some prop'; + } - let testDir: TestDir|undefined; + let testDir: TestDir|undefined; - @Directive({selector: '[test]'}) - class TestDir { - constructor() { testDir = this; } + @Directive({selector: '[test]'}) + class TestDir { + constructor() { testDir = this; } - // TODO(issue/24571): remove '!'. - @Input('test') - test !: string; - } + // TODO(issue/24571): remove '!'. + @Input('test') + test !: string; + } - TestBed.overrideTemplateUsingTestingModule( - MyComponent, '
Hello world!
'); + TestBed.overrideTemplateUsingTestingModule( + MyComponent, '
Hello world!
'); - const fixture = TestBed.configureTestingModule({declarations: [MyComponent, TestDir]}) - .createComponent(MyComponent); - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('Hello world!'); - expect(testDir).toBeAnInstanceOf(TestDir); - expect(testDir !.test).toBe('some prop'); - }); + const fixture = TestBed.configureTestingModule({declarations: [MyComponent, TestDir]}) + .createComponent(MyComponent); + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('Hello world!'); + expect(testDir).toBeAnInstanceOf(TestDir); + expect(testDir !.test).toBe('some prop'); + }); - fixmeIvy('FW-851: TestBed.overrideTemplateUsingTestingModule is not implemented') - .it('should throw if the TestBed is already created', () => { - @Component({selector: 'comp', template: 'a'}) - class MyComponent { - } + it('should throw if the TestBed is already created', () => { + @Component({selector: 'comp', template: 'a'}) + class MyComponent { + } - TestBed.get(Injector); + TestBed.get(Injector); - expect(() => TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b')) - .toThrowError( - /Cannot override template when the test module has already been instantiated/); - }); + expect(() => TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b')) + .toThrowError( + /Cannot override template when the test module has already been instantiated/); + }); - fixmeIvy('FW-851: TestBed.overrideTemplateUsingTestingModule is not implemented') - .it('should reset overrides when the testing module is resetted', () => { - @Component({selector: 'comp', template: 'a'}) - class MyComponent { - } + it('should reset overrides when the testing module is resetted', () => { + @Component({selector: 'comp', template: 'a'}) + class MyComponent { + } - TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b'); + TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b'); - const fixture = TestBed.resetTestingModule() - .configureTestingModule({declarations: [MyComponent]}) - .createComponent(MyComponent); - expect(fixture.nativeElement).toHaveText('a'); - }); + const fixture = TestBed.resetTestingModule() + .configureTestingModule({declarations: [MyComponent]}) + .createComponent(MyComponent); + expect(fixture.nativeElement).toHaveText('a'); + }); }); describe('setting up the compiler', () => {