Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve AOT unit tests #19996

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/src/core_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo
export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util';
export {makeDecorator as ɵmakeDecorator} from './util/decorators';
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
export {clearProviderOverrides as ɵclearProviderOverrides, overrideProvider as ɵoverrideProvider} from './view/index';
export {clearOverrides as ɵclearOverrides, overrideComponentView as ɵoverrideComponentView, overrideProvider as ɵoverrideProvider} from './view/index';
export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider';
12 changes: 9 additions & 3 deletions packages/core/src/view/entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,27 @@
*/

import {Injector} from '../di/injector';
import {ComponentFactory} from '../linker/component_factory';
import {NgModuleFactory, NgModuleRef} from '../linker/ng_module_factory';
import {Type} from '../type';

import {initServicesIfNeeded} from './services';
import {NgModuleDefinitionFactory, ProviderOverride, Services} from './types';
import {NgModuleDefinitionFactory, ProviderOverride, Services, ViewDefinition} from './types';
import {resolveDefinition} from './util';

export function overrideProvider(override: ProviderOverride) {
initServicesIfNeeded();
return Services.overrideProvider(override);
}

export function clearProviderOverrides() {
export function overrideComponentView(comp: Type<any>, componentFactory: ComponentFactory<any>) {
initServicesIfNeeded();
return Services.clearProviderOverrides();
return Services.overrideComponentView(comp, componentFactory);
}

export function clearOverrides() {
initServicesIfNeeded();
return Services.clearOverrides();
}

// Attention: this function is called as top level function.
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/view/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

export {anchorDef, elementDef} from './element';
export {clearProviderOverrides, createNgModuleFactory, overrideProvider} from './entrypoint';
export {clearOverrides, createNgModuleFactory, overrideComponentView, overrideProvider} from './entrypoint';
export {ngContentDef} from './ng_content';
export {moduleDef, moduleProvideDef} from './ng_module';
export {directiveDef, pipeDef, providerDef} from './provider';
Expand Down
35 changes: 26 additions & 9 deletions packages/core/src/view/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {isDevMode} from '../application_ref';
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
import {Injector} from '../di';
import {ErrorHandler} from '../error_handler';
import {ComponentFactory} from '../linker/component_factory';
import {NgModuleRef} from '../linker/ng_module_factory';
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
import {Sanitizer} from '../security';
Expand All @@ -18,9 +19,9 @@ import {Type} from '../type';
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
import {resolveDep} from './provider';
import {dirtyParentQueries, getQueryValue} from './query';
import {createInjector, createNgModuleRef} from './refs';
import {createInjector, createNgModuleRef, getComponentViewDefinitionFactory} from './refs';
import {ArgumentType, BindingFlags, CheckType, DebugContext, DepDef, ElementData, NgModuleDefinition, NgModuleProviderDef, NodeDef, NodeFlags, NodeLogger, ProviderOverride, RootData, Services, ViewData, ViewDefinition, ViewState, asElementData, asPureExpressionData} from './types';
import {NOOP, isComponentView, renderNode, splitDepsDsl, viewParentEl} from './util';
import {NOOP, isComponentView, renderNode, resolveDefinition, splitDepsDsl, viewParentEl} from './util';
import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createComponentView, createEmbeddedView, createRootView, destroyView} from './view';


Expand All @@ -38,7 +39,8 @@ export function initServicesIfNeeded() {
Services.createComponentView = services.createComponentView;
Services.createNgModuleRef = services.createNgModuleRef;
Services.overrideProvider = services.overrideProvider;
Services.clearProviderOverrides = services.clearProviderOverrides;
Services.overrideComponentView = services.overrideComponentView;
Services.clearOverrides = services.clearOverrides;
Services.checkAndUpdateView = services.checkAndUpdateView;
Services.checkNoChangesView = services.checkNoChangesView;
Services.destroyView = services.destroyView;
Expand All @@ -58,7 +60,8 @@ function createProdServices() {
createComponentView: createComponentView,
createNgModuleRef: createNgModuleRef,
overrideProvider: NOOP,
clearProviderOverrides: NOOP,
overrideComponentView: NOOP,
clearOverrides: NOOP,
checkAndUpdateView: checkAndUpdateView,
checkNoChangesView: checkNoChangesView,
destroyView: destroyView,
Expand All @@ -84,7 +87,8 @@ function createDebugServices() {
createComponentView: debugCreateComponentView,
createNgModuleRef: debugCreateNgModuleRef,
overrideProvider: debugOverrideProvider,
clearProviderOverrides: debugClearProviderOverrides,
overrideComponentView: debugOverrideComponentView,
clearOverrides: debugClearOverrides,
checkAndUpdateView: debugCheckAndUpdateView,
checkNoChangesView: debugCheckNoChangesView,
destroyView: debugDestroyView,
Expand Down Expand Up @@ -139,10 +143,15 @@ function debugCreateEmbeddedView(

function debugCreateComponentView(
parentView: ViewData, nodeDef: NodeDef, viewDef: ViewDefinition, hostElement: any): ViewData {
const defWithOverride = applyProviderOverridesToView(viewDef);
const overrideComponentView =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

override+n+CV ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already have OverrideProvider, keeping for consistency

viewDefOverrides.get(nodeDef.element !.componentProvider !.provider !.token);
if (overrideComponentView) {
viewDef = overrideComponentView;
} else {
viewDef = applyProviderOverridesToView(viewDef);
}
return callWithDebugContext(
DebugAction.create, createComponentView, null,
[parentView, nodeDef, defWithOverride, hostElement]);
DebugAction.create, createComponentView, null, [parentView, nodeDef, viewDef, hostElement]);
}

function debugCreateNgModuleRef(
Expand All @@ -153,13 +162,21 @@ function debugCreateNgModuleRef(
}

const providerOverrides = new Map<any, ProviderOverride>();
const viewDefOverrides = new Map<any, ViewDefinition>();

function debugOverrideProvider(override: ProviderOverride) {
providerOverrides.set(override.token, override);
}

function debugClearProviderOverrides() {
function debugOverrideComponentView(comp: any, compFactory: ComponentFactory<any>) {
const hostViewDef = resolveDefinition(getComponentViewDefinitionFactory(compFactory));
const compViewDef = resolveDefinition(hostViewDef.nodes[0].element !.componentView !);
viewDefOverrides.set(comp, compViewDef);
}

function debugClearOverrides() {
providerOverrides.clear();
viewDefOverrides.clear();
}

// Notes about the algorithm:
Expand Down
8 changes: 6 additions & 2 deletions packages/core/src/view/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import {Injector} from '../di';
import {ErrorHandler} from '../error_handler';
import {ComponentFactory} from '../linker/component_factory';
import {NgModuleRef} from '../linker/ng_module_factory';
import {QueryList} from '../linker/query_list';
import {TemplateRef} from '../linker/template_ref';
Expand All @@ -16,6 +17,7 @@ import {Renderer2, RendererFactory2, RendererType2} from '../render/api';
import {Sanitizer, SecurityContext} from '../security';
import {Type} from '../type';


// -------------------------------------
// Defs
// -------------------------------------
Expand Down Expand Up @@ -522,7 +524,8 @@ export interface Services {
moduleType: Type<any>, parent: Injector, bootstrapComponents: Type<any>[],
def: NgModuleDefinition): NgModuleRef<any>;
overrideProvider(override: ProviderOverride): void;
clearProviderOverrides(): void;
overrideComponentView(compType: Type<any>, compFactory: ComponentFactory<any>): void;
clearOverrides(): void;
checkAndUpdateView(view: ViewData): void;
checkNoChangesView(view: ViewData): void;
destroyView(view: ViewData): void;
Expand All @@ -547,7 +550,8 @@ export const Services: Services = {
createComponentView: undefined !,
createNgModuleRef: undefined !,
overrideProvider: undefined !,
clearProviderOverrides: undefined !,
overrideComponentView: undefined !,
clearOverrides: undefined !,
checkAndUpdateView: undefined !,
checkNoChangesView: undefined !,
destroyView: undefined !,
Expand Down
25 changes: 25 additions & 0 deletions packages/core/test/linker/jit_summaries_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {CompileMetadataResolver} from '@angular/compiler/src/metadata_resolver';
import {MockResourceLoader} from '@angular/compiler/testing/src/resource_loader_mock';
import {Component, Directive, Injectable, NgModule, Pipe, Type} from '@angular/core';
import {TestBed, async, getTestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';

export function main() {
describe('Jit Summaries', () => {
Expand Down Expand Up @@ -222,5 +223,29 @@ export function main() {
.createComponent(TestComp);
expectInstanceCreated(SomeDirective);
});

it('should allow to override a provider', () => {
resetTestEnvironmentWithSummaries(summaries);

const overwrittenValue = {};

TestBed.overrideProvider(SomeDep, {useFactory: () => overwrittenValue, deps: []});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chain call (or assert the type of the return value) ?


const fixture = TestBed.configureTestingModule({providers: [SomeDep], imports: [SomeModule]})
.createComponent(SomePublicComponent);
expect(fixture.componentInstance.dep).toBe(overwrittenValue);
});

it('should allow to override a template', () => {
resetTestEnvironmentWithSummaries(summaries);

TestBed.overrideTemplateUsingTestingModule(SomePublicComponent, 'overwritten');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't this test work the same with overrideTemplate only ?


const fixture = TestBed.configureTestingModule({providers: [SomeDep], imports: [SomeModule]})
.createComponent(SomePublicComponent);
expectInstanceCreated(SomePublicComponent);

expect(fixture.nativeElement).toHaveText('overwritten');
});
});
}
38 changes: 35 additions & 3 deletions packages/core/testing/src/test_bed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, Type, ɵDepFlags as DepFlags, ɵNodeFlags as NodeFlags, ɵclearProviderOverrides as clearProviderOverrides, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, Type, ɵDepFlags as DepFlags, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';

import {AsyncTestCompleter} from './async_test_completer';
import {ComponentFixture} from './component_fixture';
Expand Down Expand Up @@ -142,9 +142,23 @@ export class TestBed implements Injector {
return TestBed;
}

/**
* Overrides the template of the given component, compiling the template
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add docs to overrideTemplate to clearly describe the difference ?

* in the context of the TestingModule.
*
* Note: This works for JIT and AOTed components as well.
*/
static overrideTemplateUsingTestingModule(component: Type<any>, template: string):
typeof TestBed {
getTestBed().overrideTemplateUsingTestingModule(component, template);
return TestBed;
}


/**
* Overwrites all providers for the given token with the given provider definition.
*
* Note: This works for JIT and AOTed components as well.
*/
static overrideProvider(token: any, provider: {
useFactory: Function,
Expand Down Expand Up @@ -208,6 +222,7 @@ export class TestBed implements Injector {

private _testEnvAotSummaries: () => any[] = () => [];
private _aotSummaries: Array<() => any[]> = [];
private _templateOverrides: Array<{component: Type<any>, templateOf: Type<any>}> = [];

platform: PlatformRef = null !;

Expand Down Expand Up @@ -251,8 +266,9 @@ export class TestBed implements Injector {
}

resetTestingModule() {
clearProviderOverrides();
clearOverrides();
this._aotSummaries = [];
this._templateOverrides = [];
this._compiler = null !;
this._moduleOverrides = [];
this._componentOverrides = [];
Expand Down Expand Up @@ -333,6 +349,11 @@ export class TestBed implements Injector {
}
}
}
for (const {component, templateOf} of this._templateOverrides) {
const compFactory = this._compiler.getComponentFactory(templateOf);
overrideComponentView(component, compFactory);
}

const ngZone = new NgZone({enableLongStackTrace: true});
const ngZoneInjector =
Injector.create([{provide: NgZone, useValue: ngZone}], this.platform.injector);
Expand All @@ -345,7 +366,8 @@ export class TestBed implements Injector {

private _createCompilerAndModule(): Type<any> {
const providers = this._providers.concat([{provide: TestBed, useValue: this}]);
const declarations = this._declarations;
const declarations =
[...this._declarations, ...this._templateOverrides.map(entry => entry.templateOf)];
const imports = [this.ngModule, this._imports];
const schemas = this._schemas;

Expand Down Expand Up @@ -478,6 +500,16 @@ export class TestBed implements Injector {
overrideProvider({token, flags, deps, value, deprecatedBehavior: deprecated});
}

overrideTemplateUsingTestingModule(component: Type<any>, template: string) {
this._assertNotInstantiated('overrideTemplateUsingTestingModule', 'override template');

@Component({selector: 'empty', template: template})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit template: template could be simplified

class OverrideComponent {
}

this._templateOverrides.push({component, templateOf: OverrideComponent});
}

createComponent<T>(component: Type<T>): ComponentFixture<T> {
this._initIfNeeded();
const componentFactory = this._compiler.getComponentFactory(component);
Expand Down
54 changes: 54 additions & 0 deletions packages/platform-browser/test/testing_public_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,60 @@ export function main() {
});
});

describe('overrideTemplateUsingTestingModule', () => {
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;

@Directive({selector: '[test]'})
class TestDir {
constructor() { testDir = this; }

@Input('test')
test: string;
}

TestBed.overrideTemplateUsingTestingModule(
MyComponent, '<div [test]="prop">Hello world!</div>');

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');
});

it('should throw if the TestBed is already created', () => {
@Component({selector: 'comp', template: 'a'})
class MyComponent {
}

TestBed.get(Injector);

expect(() => TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b'))
.toThrowError(
/Cannot override template when the test module has already been instantiated/);
});

it('should reset overrides when the testing modules is resetted', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testing module-s-

@Component({selector: 'comp', template: 'a'})
class MyComponent {
}

TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b');
TestBed.resetTestingModule();

const fixture = TestBed.configureTestingModule({declarations: [MyComponent]})
.createComponent(MyComponent);
expect(fixture.nativeElement).toHaveText('a');
});
});

describe('setting up the compiler', () => {

describe('providers', () => {
Expand Down