feat(ivy): implement TestBed (#25369)

PR Close #25369
This commit is contained in:
Victor Berchet
2018-08-06 14:09:38 -07:00
committed by Ben Lesh
parent 85106375ac
commit 14ac7ad6b4
37 changed files with 1543 additions and 363 deletions

View File

@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {InjectionToken} from '../di/injection_token';
import {Injector, inject} from '../di/injector';
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
import {ElementRef} from '../linker/element_ref';
import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
import {RendererFactory2} from '../render/api';
import {Type} from '../type';
@ -21,7 +21,7 @@ import {LifecycleHooksFeature, createRootContext} from './component';
import {baseDirectiveCreate, createLNode, createLViewData, createTView, elementCreate, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderEmbeddedTemplate} from './instructions';
import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition';
import {LElementNode, TNode, TNodeType} from './interfaces/node';
import {RElement, domRendererFactory3} from './interfaces/renderer';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {RootViewRef, ViewRef} from './view_ref';
@ -55,8 +55,20 @@ export const ROOT_CONTEXT = new InjectionToken<RootContext>(
* A change detection scheduler token for {@link RootContext}. This token is the default value used
* for the default `RootContext` found in the {@link ROOT_CONTEXT} token.
*/
export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>(
'SCHEDULER_TOKEN', {providedIn: 'root', factory: () => requestAnimationFrame.bind(window)});
export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>('SCHEDULER_TOKEN', {
providedIn: 'root',
factory: () => {
const useRaf = typeof requestAnimationFrame !== 'undefined' && typeof window !== 'undefined';
return useRaf ? requestAnimationFrame.bind(window) : setTimeout;
},
});
/**
* A function used to wrap the `RendererFactory2`.
* Used in tests to change the `RendererFactory2` into a `DebugRendererFactory2`.
*/
export const WRAP_RENDERER_FACTORY2 =
new InjectionToken<(rf: RendererFactory2) => RendererFactory2>('WRAP_RENDERER_FACTORY2');
/**
* Render3 implementation of {@link viewEngine_ComponentFactory}.
@ -65,9 +77,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
selector: string;
componentType: Type<any>;
ngContentSelectors: string[];
get inputs(): {propName: string; templateName: string;}[] {
return toRefArray(this.componentDef.inputs);
}
get outputs(): {propName: string; templateName: string;}[] {
return toRefArray(this.componentDef.outputs);
}
@ -84,8 +98,15 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
ngModule?: viewEngine_NgModuleRef<any>|undefined): viewEngine_ComponentRef<T> {
const isInternalRootView = rootSelectorOrNode === undefined;
const rendererFactory =
ngModule ? ngModule.injector.get(RendererFactory2) : domRendererFactory3;
let rendererFactory: RendererFactory3;
if (ngModule) {
const wrapper = ngModule.injector.get(WRAP_RENDERER_FACTORY2, (v: RendererFactory2) => v);
rendererFactory = wrapper(ngModule.injector.get(RendererFactory2)) as RendererFactory3;
} else {
rendererFactory = domRendererFactory3;
}
const hostNode = isInternalRootView ?
elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef)) :
locateHostElement(rendererFactory, rootSelectorOrNode);
@ -116,11 +137,10 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
elementNode = hostElement(componentTag, hostNode, this.componentDef);
// Create directive instance with factory() and store at index 0 in directives array
rootContext.components.push(
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef) as T);
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef);
rootContext.components.push(component);
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
(elementNode.data as LViewData)[CONTEXT] = component;
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
// executed here?
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
@ -177,11 +197,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
*/
export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
destroyCbs: (() => void)[]|null = [];
location: ElementRef<any>;
location: viewEngine_ElementRef<any>;
injector: Injector;
instance: T;
hostView: ViewRef<T>;
changeDetectorRef: ChangeDetectorRef;
changeDetectorRef: ViewEngine_ChangeDetectorRef;
componentType: Type<T>;
constructor(
@ -192,7 +212,7 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
this.hostView = this.changeDetectorRef = new RootViewRef<T>(rootView);
this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView);
this.injector = injector;
this.location = new ElementRef(hostNode);
this.location = new viewEngine_ElementRef(hostNode);
this.componentType = componentType;
}
@ -205,4 +225,4 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
this.destroyCbs !.push(callback);
}
}
}