fix(ivy): implement 'TestBed.overrideTemplateUsingTestingModule' function (#27717)
Adding 'TestBed.overrideTemplateUsingTestingModule' function that overrides Component template and uses TestingModule as a scope. PR Close #27717
This commit is contained in:
parent
176b3f12f4
commit
38b4d15227
|
@ -124,7 +124,11 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideTemplateUsingTestingModule(component: Type<any>, template: string): void {
|
overrideTemplateUsingTestingModule(component: Type<any>, 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: {
|
static overrideProvider(token: any, provider: {
|
||||||
|
@ -185,6 +189,7 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||||
private _providerOverrides: Provider[] = [];
|
private _providerOverrides: Provider[] = [];
|
||||||
private _rootProviderOverrides: Provider[] = [];
|
private _rootProviderOverrides: Provider[] = [];
|
||||||
private _providerOverridesByToken: Map<any, Provider[]> = new Map();
|
private _providerOverridesByToken: Map<any, Provider[]> = new Map();
|
||||||
|
private _templateOverrides: Map<Type<any>, string> = new Map();
|
||||||
|
|
||||||
// test module configuration
|
// test module configuration
|
||||||
private _providers: Provider[] = [];
|
private _providers: Provider[] = [];
|
||||||
|
@ -195,6 +200,7 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||||
private _activeFixtures: ComponentFixture<any>[] = [];
|
private _activeFixtures: ComponentFixture<any>[] = [];
|
||||||
|
|
||||||
private _moduleRef: NgModuleRef<any> = null !;
|
private _moduleRef: NgModuleRef<any> = null !;
|
||||||
|
private _testModuleType: NgModuleType<any> = null !;
|
||||||
|
|
||||||
private _instantiated: boolean = false;
|
private _instantiated: boolean = false;
|
||||||
|
|
||||||
|
@ -241,6 +247,7 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||||
this._providerOverrides = [];
|
this._providerOverrides = [];
|
||||||
this._rootProviderOverrides = [];
|
this._rootProviderOverrides = [];
|
||||||
this._providerOverridesByToken.clear();
|
this._providerOverridesByToken.clear();
|
||||||
|
this._templateOverrides.clear();
|
||||||
|
|
||||||
// reset test module config
|
// reset test module config
|
||||||
this._providers = [];
|
this._providers = [];
|
||||||
|
@ -248,6 +255,7 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||||
this._imports = [];
|
this._imports = [];
|
||||||
this._schemas = [];
|
this._schemas = [];
|
||||||
this._moduleRef = null !;
|
this._moduleRef = null !;
|
||||||
|
this._testModuleType = null !;
|
||||||
|
|
||||||
this._instantiated = false;
|
this._instantiated = false;
|
||||||
this._activeFixtures.forEach((fixture) => {
|
this._activeFixtures.forEach((fixture) => {
|
||||||
|
@ -402,11 +410,11 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvers = this._getResolvers();
|
const resolvers = this._getResolvers();
|
||||||
const testModuleType = this._createTestModule();
|
this._testModuleType = this._createTestModule();
|
||||||
this._compileNgModule(testModuleType, resolvers);
|
this._compileNgModule(this._testModuleType, resolvers);
|
||||||
|
|
||||||
const parentInjector = this.platform.injector;
|
const parentInjector = this.platform.injector;
|
||||||
this._moduleRef = new NgModuleRef(testModuleType, parentInjector);
|
this._moduleRef = new NgModuleRef(this._testModuleType, parentInjector);
|
||||||
|
|
||||||
// ApplicationInitStatus.runInitializers() is marked @internal
|
// ApplicationInitStatus.runInitializers() is marked @internal
|
||||||
// to core. Cast it to any before accessing it.
|
// to core. Cast it to any before accessing it.
|
||||||
|
@ -472,15 +480,20 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||||
return DynamicTestModule as NgModuleType;
|
return DynamicTestModule as NgModuleType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getMetaWithOverrides(meta: Component|Directive|NgModule) {
|
private _getMetaWithOverrides(meta: Component|Directive|NgModule, type?: Type<any>) {
|
||||||
|
const overrides: {providers?: any[], template?: string} = {};
|
||||||
if (meta.providers && meta.providers.length) {
|
if (meta.providers && meta.providers.length) {
|
||||||
const overrides =
|
const providerOverrides =
|
||||||
flatten(meta.providers, (provider: any) => this._getProviderOverrides(provider));
|
flatten(meta.providers, (provider: any) => this._getProviderOverrides(provider));
|
||||||
if (overrides.length) {
|
if (providerOverrides.length) {
|
||||||
return {...meta, providers: [...meta.providers, ...overrides]};
|
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 {
|
private _compileNgModule(moduleType: NgModuleType, resolvers: Resolvers): void {
|
||||||
|
@ -501,7 +514,7 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||||
declarations.forEach(declaration => {
|
declarations.forEach(declaration => {
|
||||||
const component = resolvers.component.resolve(declaration);
|
const component = resolvers.component.resolve(declaration);
|
||||||
if (component) {
|
if (component) {
|
||||||
const metadata = this._getMetaWithOverrides(component);
|
const metadata = this._getMetaWithOverrides(component, declaration);
|
||||||
compileComponent(declaration, metadata);
|
compileComponent(declaration, metadata);
|
||||||
compiledComponents.push(declaration);
|
compiledComponents.push(declaration);
|
||||||
return;
|
return;
|
||||||
|
@ -523,8 +536,15 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||||
|
|
||||||
// Compile transitive modules, components, directives and pipes
|
// Compile transitive modules, components, directives and pipes
|
||||||
const transitiveScope = this._transitiveScopesFor(moduleType, resolvers);
|
const transitiveScope = this._transitiveScopesFor(moduleType, resolvers);
|
||||||
compiledComponents.forEach(
|
compiledComponents.forEach(cmp => {
|
||||||
cmp => patchComponentDefWithScope((cmp as any).ngComponentDef, transitiveScope));
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -727,61 +727,58 @@ class CompWithUrlTemplate {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('overrideTemplateUsingTestingModule', () => {
|
describe('overrideTemplateUsingTestingModule', () => {
|
||||||
fixmeIvy('FW-851: TestBed.overrideTemplateUsingTestingModule is not implemented')
|
it('should compile the template in the context of the testing module', () => {
|
||||||
.it('should compile the template in the context of the testing module', () => {
|
@Component({selector: 'comp', template: 'a'})
|
||||||
@Component({selector: 'comp', template: 'a'})
|
class MyComponent {
|
||||||
class MyComponent {
|
prop = 'some prop';
|
||||||
prop = 'some prop';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let testDir: TestDir|undefined;
|
let testDir: TestDir|undefined;
|
||||||
|
|
||||||
@Directive({selector: '[test]'})
|
@Directive({selector: '[test]'})
|
||||||
class TestDir {
|
class TestDir {
|
||||||
constructor() { testDir = this; }
|
constructor() { testDir = this; }
|
||||||
|
|
||||||
// TODO(issue/24571): remove '!'.
|
// TODO(issue/24571): remove '!'.
|
||||||
@Input('test')
|
@Input('test')
|
||||||
test !: string;
|
test !: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.overrideTemplateUsingTestingModule(
|
TestBed.overrideTemplateUsingTestingModule(
|
||||||
MyComponent, '<div [test]="prop">Hello world!</div>');
|
MyComponent, '<div [test]="prop">Hello world!</div>');
|
||||||
|
|
||||||
const fixture = TestBed.configureTestingModule({declarations: [MyComponent, TestDir]})
|
const fixture = TestBed.configureTestingModule({declarations: [MyComponent, TestDir]})
|
||||||
.createComponent(MyComponent);
|
.createComponent(MyComponent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.nativeElement).toHaveText('Hello world!');
|
expect(fixture.nativeElement).toHaveText('Hello world!');
|
||||||
expect(testDir).toBeAnInstanceOf(TestDir);
|
expect(testDir).toBeAnInstanceOf(TestDir);
|
||||||
expect(testDir !.test).toBe('some prop');
|
expect(testDir !.test).toBe('some prop');
|
||||||
});
|
});
|
||||||
|
|
||||||
fixmeIvy('FW-851: TestBed.overrideTemplateUsingTestingModule is not implemented')
|
it('should throw if the TestBed is already created', () => {
|
||||||
.it('should throw if the TestBed is already created', () => {
|
@Component({selector: 'comp', template: 'a'})
|
||||||
@Component({selector: 'comp', template: 'a'})
|
class MyComponent {
|
||||||
class MyComponent {
|
}
|
||||||
}
|
|
||||||
|
|
||||||
TestBed.get(Injector);
|
TestBed.get(Injector);
|
||||||
|
|
||||||
expect(() => TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b'))
|
expect(() => TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b'))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
/Cannot override template when the test module has already been instantiated/);
|
/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', () => {
|
||||||
.it('should reset overrides when the testing module is resetted', () => {
|
@Component({selector: 'comp', template: 'a'})
|
||||||
@Component({selector: 'comp', template: 'a'})
|
class MyComponent {
|
||||||
class MyComponent {
|
}
|
||||||
}
|
|
||||||
|
|
||||||
TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b');
|
TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b');
|
||||||
|
|
||||||
const fixture = TestBed.resetTestingModule()
|
const fixture = TestBed.resetTestingModule()
|
||||||
.configureTestingModule({declarations: [MyComponent]})
|
.configureTestingModule({declarations: [MyComponent]})
|
||||||
.createComponent(MyComponent);
|
.createComponent(MyComponent);
|
||||||
expect(fixture.nativeElement).toHaveText('a');
|
expect(fixture.nativeElement).toHaveText('a');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setting up the compiler', () => {
|
describe('setting up the compiler', () => {
|
||||||
|
|
Loading…
Reference in New Issue