diff --git a/packages/common/test/directives/ng_component_outlet_spec.ts b/packages/common/test/directives/ng_component_outlet_spec.ts index adc1f6023f..7f0b2fc08a 100644 --- a/packages/common/test/directives/ng_component_outlet_spec.ts +++ b/packages/common/test/directives/ng_component_outlet_spec.ts @@ -144,22 +144,21 @@ describe('insert/remove', () => { expect(fixture.nativeElement).toHaveText('projected foo'); })); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should resolve components from other modules, if supplied', async(() => { - const compiler = TestBed.get(Compiler) as Compiler; - let fixture = TestBed.createComponent(TestComponent); + it('should resolve components from other modules, if supplied', async(() => { + const compiler = TestBed.get(Compiler) as Compiler; + let fixture = TestBed.createComponent(TestComponent); - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText(''); + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText(''); - fixture.componentInstance.module = compiler.compileModuleSync(TestModule2); - fixture.componentInstance.currentComponent = Module2InjectedComponent; + fixture.componentInstance.module = compiler.compileModuleSync(TestModule2); + fixture.componentInstance.currentComponent = Module2InjectedComponent; - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('baz'); - })); + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('baz'); + })); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-739: destroy on NgModuleRef is not being called') && it('should clean up moduleRef, if supplied', async(() => { let destroyed = false; const compiler = TestBed.get(Compiler) as Compiler; @@ -176,40 +175,38 @@ describe('insert/remove', () => { expect(moduleRef.destroy).toHaveBeenCalled(); })); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should not re-create moduleRef when it didn\'t actually change', async(() => { - const compiler = TestBed.get(Compiler) as Compiler; - const fixture = TestBed.createComponent(TestComponent); + it('should not re-create moduleRef when it didn\'t actually change', async(() => { + const compiler = TestBed.get(Compiler) as Compiler; + const fixture = TestBed.createComponent(TestComponent); - fixture.componentInstance.module = compiler.compileModuleSync(TestModule2); - fixture.componentInstance.currentComponent = Module2InjectedComponent; - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('baz'); - const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef']; + fixture.componentInstance.module = compiler.compileModuleSync(TestModule2); + fixture.componentInstance.currentComponent = Module2InjectedComponent; + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('baz'); + const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef']; - fixture.componentInstance.currentComponent = Module2InjectedComponent2; - fixture.detectChanges(); + fixture.componentInstance.currentComponent = Module2InjectedComponent2; + fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('baz2'); - expect(moduleRef).toBe(fixture.componentInstance.ngComponentOutlet['_moduleRef']); - })); + expect(fixture.nativeElement).toHaveText('baz2'); + expect(moduleRef).toBe(fixture.componentInstance.ngComponentOutlet['_moduleRef']); + })); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should re-create moduleRef when changed', async(() => { - const compiler = TestBed.get(Compiler) as Compiler; - const fixture = TestBed.createComponent(TestComponent); - fixture.componentInstance.module = compiler.compileModuleSync(TestModule2); - fixture.componentInstance.currentComponent = Module2InjectedComponent; - fixture.detectChanges(); + it('should re-create moduleRef when changed', async(() => { + const compiler = TestBed.get(Compiler) as Compiler; + const fixture = TestBed.createComponent(TestComponent); + fixture.componentInstance.module = compiler.compileModuleSync(TestModule2); + fixture.componentInstance.currentComponent = Module2InjectedComponent; + fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('baz'); + expect(fixture.nativeElement).toHaveText('baz'); - fixture.componentInstance.module = compiler.compileModuleSync(TestModule3); - fixture.componentInstance.currentComponent = Module3InjectedComponent; - fixture.detectChanges(); + fixture.componentInstance.module = compiler.compileModuleSync(TestModule3); + fixture.componentInstance.currentComponent = Module3InjectedComponent; + fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('bat'); - })); + expect(fixture.nativeElement).toHaveText('bat'); + })); }); const TEST_TOKEN = new InjectionToken('TestToken'); diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 8f815a5faa..cec987d2e8 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -114,12 +114,9 @@ export { i18nEnd as ɵi18nEnd, i18nApply as ɵi18nApply, i18nPostprocess as ɵi18nPostprocess, - WRAP_RENDERER_FACTORY2 as ɵWRAP_RENDERER_FACTORY2, setClassMetadata as ɵsetClassMetadata, } from './render3/index'; -export { Render3DebugRendererFactory2 as ɵRender3DebugRendererFactory2 } from './render3/debug'; - export { compileComponent as ɵcompileComponent, diff --git a/packages/core/src/debug/debug_node.ts b/packages/core/src/debug/debug_node.ts index 06ccb62ad1..640a4e4f88 100644 --- a/packages/core/src/debug/debug_node.ts +++ b/packages/core/src/debug/debug_node.ts @@ -7,6 +7,11 @@ */ import {Injector} from '../di'; +import {DirectiveDef} from '../render3'; +import {assertDomNode} from '../render3/assert'; +import {getComponent, getInjector, getLocalRefs, loadContext} from '../render3/discovery_utils'; +import {TNode, TNodeFlags} from '../render3/interfaces/node'; +import {TVIEW} from '../render3/interfaces/view'; import {DebugContext} from '../view/index'; export class EventListener { @@ -16,12 +21,26 @@ export class EventListener { /** * @publicApi */ -export class DebugNode { - listeners: EventListener[] = []; - parent: DebugElement|null = null; +export interface DebugNode { + readonly listeners: EventListener[]; + readonly parent: DebugElement|null; + readonly nativeNode: any; + readonly injector: Injector; + readonly componentInstance: any; + readonly context: any; + readonly references: {[key: string]: any}; + readonly providerTokens: any[]; +} +export class DebugNode__PRE_R3__ { + readonly listeners: EventListener[] = []; + readonly parent: DebugElement|null = null; + readonly nativeNode: any; + private readonly _debugContext: DebugContext; - constructor(public nativeNode: any, parent: DebugNode|null, private _debugContext: DebugContext) { - if (parent && parent instanceof DebugElement) { + constructor(nativeNode: any, parent: DebugNode|null, _debugContext: DebugContext) { + this._debugContext = _debugContext; + this.nativeNode = nativeNode; + if (parent && parent instanceof DebugElement__PRE_R3__) { parent.addChild(this); } } @@ -40,14 +59,29 @@ export class DebugNode { /** * @publicApi */ -export class DebugElement extends DebugNode { - name !: string; - properties: {[key: string]: any} = {}; - attributes: {[key: string]: string | null} = {}; - classes: {[key: string]: boolean} = {}; - styles: {[key: string]: string | null} = {}; - childNodes: DebugNode[] = []; - nativeElement: any; +export interface DebugElement extends DebugNode { + readonly name: string; + readonly properties: {[key: string]: any}; + readonly attributes: {[key: string]: string | null}; + readonly classes: {[key: string]: boolean}; + readonly styles: {[key: string]: string | null}; + readonly childNodes: DebugNode[]; + readonly nativeElement: any; + readonly children: DebugElement[]; + + query(predicate: Predicate): DebugElement; + queryAll(predicate: Predicate): DebugElement[]; + queryAllNodes(predicate: Predicate): DebugNode[]; + triggerEventHandler(eventName: string, eventObj: any): void; +} +export class DebugElement__PRE_R3__ extends DebugNode__PRE_R3__ implements DebugElement { + readonly name !: string; + readonly properties: {[key: string]: any} = {}; + readonly attributes: {[key: string]: string | null} = {}; + readonly classes: {[key: string]: boolean} = {}; + readonly styles: {[key: string]: string | null} = {}; + readonly childNodes: DebugNode[] = []; + readonly nativeElement: any; constructor(nativeNode: any, parent: any, _debugContext: DebugContext) { super(nativeNode, parent, _debugContext); @@ -57,14 +91,14 @@ export class DebugElement extends DebugNode { addChild(child: DebugNode) { if (child) { this.childNodes.push(child); - child.parent = this; + (child as{parent: DebugNode}).parent = this; } } removeChild(child: DebugNode) { const childIndex = this.childNodes.indexOf(child); if (childIndex !== -1) { - child.parent = null; + (child as{parent: DebugNode | null}).parent = null; this.childNodes.splice(childIndex, 1); } } @@ -75,9 +109,9 @@ export class DebugElement extends DebugNode { this.childNodes.splice(siblingIndex + 1, 0, ...newChildren); newChildren.forEach(c => { if (c.parent) { - c.parent.removeChild(c); + (c.parent as DebugElement__PRE_R3__).removeChild(c); } - c.parent = this; + (child as{parent: DebugNode}).parent = this; }); } } @@ -88,9 +122,9 @@ export class DebugElement extends DebugNode { this.addChild(newChild); } else { if (newChild.parent) { - newChild.parent.removeChild(newChild); + (newChild.parent as DebugElement__PRE_R3__).removeChild(newChild); } - newChild.parent = this; + (newChild as{parent: DebugNode}).parent = this; this.childNodes.splice(refIndex, 0, newChild); } } @@ -113,7 +147,9 @@ export class DebugElement extends DebugNode { } get children(): DebugElement[] { - return this.childNodes.filter((node) => node instanceof DebugElement) as DebugElement[]; + return this + .childNodes // + .filter((node) => node instanceof DebugElement__PRE_R3__) as DebugElement[]; } triggerEventHandler(eventName: string, eventObj: any) { @@ -135,7 +171,7 @@ export function asNativeElements(debugEls: DebugElement[]): any { function _queryElementChildren( element: DebugElement, predicate: Predicate, matches: DebugElement[]) { element.childNodes.forEach(node => { - if (node instanceof DebugElement) { + if (node instanceof DebugElement__PRE_R3__) { if (predicate(node)) { matches.push(node); } @@ -146,27 +182,215 @@ function _queryElementChildren( function _queryNodeChildren( parentNode: DebugNode, predicate: Predicate, matches: DebugNode[]) { - if (parentNode instanceof DebugElement) { + if (parentNode instanceof DebugElement__PRE_R3__) { parentNode.childNodes.forEach(node => { if (predicate(node)) { matches.push(node); } - if (node instanceof DebugElement) { + if (node instanceof DebugElement__PRE_R3__) { _queryNodeChildren(node, predicate, matches); } }); } } +function notImplemented(): Error { + throw new Error('Missing proper ivy implementation.'); +} + +class DebugNode__POST_R3__ implements DebugNode { + readonly nativeNode: Node; + + constructor(nativeNode: Node) { this.nativeNode = nativeNode; } + + get parent(): DebugElement|null { + const parent = this.nativeNode.parentNode as HTMLElement; + return parent ? new DebugElement__POST_R3__(parent) : null; + } + + get injector(): Injector { return getInjector(this.nativeNode); } + + get componentInstance(): any { + const nativeElement = this.nativeNode; + return nativeElement && getComponent(nativeElement as HTMLElement); + } + get context(): any { + // https://angular-team.atlassian.net/browse/FW-719 + throw notImplemented(); + } + + get listeners(): EventListener[] { + // TODO: add real implementation; + // https://angular-team.atlassian.net/browse/FW-719 + return []; + } + + get references(): {[key: string]: any;} { return getLocalRefs(this.nativeNode); } + + get providerTokens(): any[] { + // TODO move to discoverable utils + const context = loadContext(this.nativeNode as HTMLElement, false) !; + if (!context) return []; + const lView = context.lViewData; + const tView = lView[TVIEW]; + const tNode = tView.data[context.nodeIndex] as TNode; + const providerTokens: any[] = []; + const nodeFlags = tNode.flags; + const startIndex = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift; + const directiveCount = nodeFlags & TNodeFlags.DirectiveCountMask; + const endIndex = startIndex + directiveCount; + for (let i = startIndex; i < endIndex; i++) { + let value = tView.data[i]; + if (isDirectiveDefHack(value)) { + // The fact that we sometimes store Type and sometimes DirectiveDef in this location is a + // design flaw. We should always store same type so that we can be monomorphic. The issue + // is that for Components/Directives we store the def instead the type. The correct behavior + // is that we should always be storing injectable type in this location. + value = value.type; + } + providerTokens.push(value); + } + return providerTokens; + } +} + +class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugElement { + constructor(nativeNode: Element) { + ngDevMode && assertDomNode(nativeNode); + super(nativeNode); + } + + get nativeElement(): Element|null { + return this.nativeNode.nodeType == Node.ELEMENT_NODE ? this.nativeNode as Element : null; + } + + get name(): string { return (this.nativeElement as HTMLElement).nodeName; } + + get properties(): {[key: string]: any;} { + const context = loadContext(this.nativeNode) !; + const lView = context.lViewData; + const tView = lView[TVIEW]; + const tNode = tView.data[context.nodeIndex] as TNode; + const properties = {}; + // TODO: https://angular-team.atlassian.net/browse/FW-681 + // Missing implementation here... + return properties; + } + + get attributes(): {[key: string]: string | null;} { + // https://angular-team.atlassian.net/browse/FW-719 + throw notImplemented(); + } + + get classes(): {[key: string]: boolean;} { + // https://angular-team.atlassian.net/browse/FW-719 + throw notImplemented(); + } + + get styles(): {[key: string]: string | null;} { + // https://angular-team.atlassian.net/browse/FW-719 + throw notImplemented(); + } + + get childNodes(): DebugNode[] { + const childNodes = this.nativeNode.childNodes; + const children: DebugNode[] = []; + for (let i = 0; i < childNodes.length; i++) { + const element = childNodes[i]; + children.push(getDebugNode__POST_R3__(element)); + } + return children; + } + + get children(): DebugElement[] { + const nativeElement = this.nativeElement; + if (!nativeElement) return []; + const childNodes = nativeElement.children; + const children: DebugElement[] = []; + for (let i = 0; i < childNodes.length; i++) { + const element = childNodes[i]; + children.push(getDebugNode__POST_R3__(element)); + } + return children; + } + + query(predicate: Predicate): DebugElement { + const results = this.queryAll(predicate); + return results[0] || null; + } + + queryAll(predicate: Predicate): DebugElement[] { + const matches: DebugElement[] = []; + _queryNodeChildrenR3(this, predicate, matches, true); + return matches; + } + + queryAllNodes(predicate: Predicate): DebugNode[] { + const matches: DebugNode[] = []; + _queryNodeChildrenR3(this, predicate, matches, false); + return matches; + } + + triggerEventHandler(eventName: string, eventObj: any): void { + // This is a hack implementation. The correct implementation would bypass the DOM and `TNode` + // information to invoke the listeners directly. + // https://angular-team.atlassian.net/browse/FW-719 + const event = document.createEvent('MouseEvent'); + event.initEvent(eventName, true, true); + (this.nativeElement as HTMLElement).dispatchEvent(event); + } +} + +/** + * This function should not exist because it is megamorphic and only mostly correct. + * + * See call site for more info. + */ +function isDirectiveDefHack(obj: any): obj is DirectiveDef { + return obj.type !== undefined && obj.template !== undefined && obj.declaredInputs !== undefined; +} + +function _queryNodeChildrenR3( + parentNode: DebugNode, predicate: Predicate, matches: DebugNode[], + elementsOnly: boolean) { + if (parentNode instanceof DebugElement__POST_R3__) { + parentNode.childNodes.forEach(node => { + if (predicate(node)) { + matches.push(node); + } + if (node instanceof DebugElement__POST_R3__) { + if (elementsOnly ? node.nativeElement : true) { + _queryNodeChildrenR3(node, predicate, matches, elementsOnly); + } + } + }); + } +} + + // Need to keep the nodes in a global Map so that multiple angular apps are supported. const _nativeNodeToDebugNode = new Map(); +function getDebugNode__PRE_R3__(nativeNode: any): DebugNode|null { + return _nativeNodeToDebugNode.get(nativeNode) || null; +} + +export function getDebugNode__POST_R3__(nativeNode: Element): DebugElement__POST_R3__; +export function getDebugNode__POST_R3__(nativeNode: Node): DebugNode__POST_R3__; +export function getDebugNode__POST_R3__(nativeNode: null): null; +export function getDebugNode__POST_R3__(nativeNode: any): DebugNode|null { + if (nativeNode instanceof Node) { + return nativeNode.nodeType == Node.ELEMENT_NODE ? + new DebugElement__POST_R3__(nativeNode as Element) : + new DebugNode__POST_R3__(nativeNode); + } + return null; +} + /** * @publicApi */ -export function getDebugNode(nativeNode: any): DebugNode|null { - return _nativeNodeToDebugNode.get(nativeNode) || null; -} +export const getDebugNode: (nativeNode: any) => DebugNode | null = getDebugNode__PRE_R3__; export function getAllDebugNodes(): DebugNode[] { return Array.from(_nativeNodeToDebugNode.values()); @@ -187,3 +411,13 @@ export function removeDebugNodeFromIndex(node: DebugNode) { * @publicApi */ export interface Predicate { (value: T): boolean; } + +/** + * @publicApi + */ +export const DebugNode: {new (...args: any[]): DebugNode} = DebugNode__PRE_R3__ as any; + +/** + * @publicApi + */ +export const DebugElement: {new (...args: any[]): DebugElement} = DebugElement__PRE_R3__ as any; diff --git a/packages/core/src/linker/compiler.ts b/packages/core/src/linker/compiler.ts index 10ccd01df5..f5e8e4e1a8 100644 --- a/packages/core/src/linker/compiler.ts +++ b/packages/core/src/linker/compiler.ts @@ -11,6 +11,7 @@ import {InjectionToken} from '../di/injection_token'; import {StaticProvider} from '../di/provider'; import {MissingTranslationStrategy} from '../i18n/tokens'; import {ViewEncapsulation} from '../metadata'; +import {NgModuleFactory as NgModuleFactoryR3} from '../render3/ng_module_ref'; import {Type} from '../type'; import {ComponentFactory} from './component_factory'; @@ -34,6 +35,42 @@ function _throwError() { throw new Error(`Runtime compiler is not loaded`); } +const Compiler_compileModuleSync__PRE_R3__: (moduleType: Type) => NgModuleFactory = + _throwError as any; +export const Compiler_compileModuleSync__POST_R3__: (moduleType: Type) => + NgModuleFactory = function(moduleType: Type): NgModuleFactory { + return new NgModuleFactoryR3(moduleType); +}; +const Compiler_compileModuleSync = Compiler_compileModuleSync__PRE_R3__; + +const Compiler_compileModuleAsync__PRE_R3__: (moduleType: Type) => + Promise> = _throwError as any; +export const Compiler_compileModuleAsync__POST_R3__: (moduleType: Type) => + Promise> = function(moduleType: Type): Promise> { + return Promise.resolve(Compiler_compileModuleSync__POST_R3__(moduleType)); +}; +const Compiler_compileModuleAsync = Compiler_compileModuleAsync__PRE_R3__; + +const Compiler_compileModuleAndAllComponentsSync__PRE_R3__: (moduleType: Type) => + ModuleWithComponentFactories = _throwError as any; +export const Compiler_compileModuleAndAllComponentsSync__POST_R3__: (moduleType: Type) => + ModuleWithComponentFactories = function(moduleType: Type): + ModuleWithComponentFactories { + return new ModuleWithComponentFactories(Compiler_compileModuleSync__POST_R3__(moduleType), []); +}; +const Compiler_compileModuleAndAllComponentsSync = + Compiler_compileModuleAndAllComponentsSync__PRE_R3__; + +const Compiler_compileModuleAndAllComponentsAsync__PRE_R3__: (moduleType: Type) => + Promise> = _throwError as any; +export const Compiler_compileModuleAndAllComponentsAsync__POST_R3__: (moduleType: Type) => + Promise> = function(moduleType: Type): + Promise> { + return Promise.resolve(Compiler_compileModuleAndAllComponentsSync__POST_R3__(moduleType)); +}; +const Compiler_compileModuleAndAllComponentsAsync = + Compiler_compileModuleAndAllComponentsAsync__PRE_R3__; + /** * Low-level service for running the angular compiler during runtime * to create {@link ComponentFactory}s, which @@ -51,27 +88,25 @@ export class Compiler { * Compiles the given NgModule and all of its components. All templates of the components listed * in `entryComponents` have to be inlined. */ - compileModuleSync(moduleType: Type): NgModuleFactory { throw _throwError(); } + compileModuleSync: (moduleType: Type) => NgModuleFactory = Compiler_compileModuleSync; /** * Compiles the given NgModule and all of its components */ - compileModuleAsync(moduleType: Type): Promise> { throw _throwError(); } + compileModuleAsync: + (moduleType: Type) => Promise> = Compiler_compileModuleAsync; /** * Same as {@link #compileModuleSync} but also creates ComponentFactories for all components. */ - compileModuleAndAllComponentsSync(moduleType: Type): ModuleWithComponentFactories { - throw _throwError(); - } + compileModuleAndAllComponentsSync: (moduleType: Type) => ModuleWithComponentFactories = + Compiler_compileModuleAndAllComponentsSync; /** * Same as {@link #compileModuleAsync} but also creates ComponentFactories for all components. */ - compileModuleAndAllComponentsAsync(moduleType: Type): - Promise> { - throw _throwError(); - } + compileModuleAndAllComponentsAsync: (moduleType: Type) => + Promise> = Compiler_compileModuleAndAllComponentsAsync; /** * Clears all caches. diff --git a/packages/core/src/metadata/di.ts b/packages/core/src/metadata/di.ts index 6793b32706..5e6090b258 100644 --- a/packages/core/src/metadata/di.ts +++ b/packages/core/src/metadata/di.ts @@ -90,7 +90,7 @@ export interface AttributeDecorator { * ] * ``` * - * + * @publicApi */ (name: string): any; new (name: string): Attribute; diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index e20ec0c258..2a114899df 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -83,3 +83,7 @@ function throwError(msg: string): never { debugger; // Left intentionally for better debugger experience. throw new Error(`ASSERTION ERROR: ${msg}`); } + +export function assertDomNode(node: any) { + assertEqual(node instanceof Node, true, 'The provided value must be an instance of a DOM Node'); +} diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index d420894e03..af42ab2980 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -13,10 +13,8 @@ import {Injector} from '../di/injector'; import {Sanitizer} from '../sanitization/security'; import {assertComponentType, assertDefined} from './assert'; -import {getContext} from './context_discovery'; import {getComponentDef} from './definition'; import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di'; -import {getHostElement} from './discovery_utils'; import {publishDefaultGlobalUtils} from './global_utils'; import {queueInitHooks, queueLifecycleHooks} from './hooks'; import {CLEAN_PROMISE, createLViewData, createNodeAtIndex, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, prefillHostVars, queueComponentIndexForCheck, refreshDescendantViews} from './instructions'; @@ -26,7 +24,7 @@ import {PlayerHandler} from './interfaces/player'; import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {CONTEXT, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view'; import {enterView, leaveView, resetComponentState} from './state'; -import {defaultScheduler, getRootView, readElementValue, readPatchedLViewData, stringify} from './util'; +import {defaultScheduler, getRootView, readPatchedLViewData, stringify} from './util'; @@ -123,15 +121,15 @@ export function renderComponent( const renderer = rendererFactory.createRenderer(hostRNode, componentDef); const rootView: LViewData = createLViewData( - null, renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, - undefined, opts.injector || null); + null, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, rendererFactory, + renderer, undefined, opts.injector || null); const oldView = enterView(rootView, null); let component: T; try { if (rendererFactory.begin) rendererFactory.begin(); - const componentView = - createRootComponentView(hostRNode, componentDef, rootView, renderer, sanitizer); + const componentView = createRootComponentView( + hostRNode, componentDef, rootView, rendererFactory, renderer, sanitizer); component = createRootComponent( componentView, componentDef, rootView, rootContext, opts.hostFeatures || null); @@ -156,15 +154,17 @@ export function renderComponent( * @returns Component view created */ export function createRootComponentView( - rNode: RElement | null, def: ComponentDef, rootView: LViewData, renderer: Renderer3, + rNode: RElement | null, def: ComponentDef, rootView: LViewData, + rendererFactory: RendererFactory3, renderer: Renderer3, sanitizer?: Sanitizer | null): LViewData { resetComponentState(); const tView = rootView[TVIEW]; const componentView = createLViewData( - rootView, renderer, + rootView, getOrCreateTView( def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery), - null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer); + null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rendererFactory, renderer, + sanitizer); const tNode = createNodeAtIndex(0, TNodeType.Element, rNode, null, null); if (tView.firstTemplatePass) { diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 0462394ac9..5da161ecc4 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -15,6 +15,7 @@ import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '. 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 {Sanitizer} from '../sanitization/security'; import {Type} from '../type'; import {VERSION} from '../version'; import {assertComponentType, assertDefined} from './assert'; @@ -25,6 +26,7 @@ import {createLViewData, createNodeAtIndex, createTView, createViewNode, element import {ComponentDef, RenderFlags} from './interfaces/definition'; import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; import {RElement, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer'; +import {SanitizerFn} from './interfaces/sanitization'; import {HEADER_OFFSET, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view'; import {enterView, leaveView} from './state'; import {defaultScheduler, getTNode} from './util'; @@ -66,13 +68,6 @@ export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>('SCHEDUL factory: () => defaultScheduler, }); -/** - * 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'); - const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {}; function createChainedInjector(rootViewInjector: Injector, moduleInjector: Injector): Injector { @@ -121,10 +116,11 @@ export class ComponentFactory extends viewEngine_ComponentFactory { const isInternalRootView = rootSelectorOrNode === undefined; let rendererFactory: RendererFactory3; + let sanitizer: Sanitizer|null = null; if (ngModule) { - const wrapper = ngModule.injector.get(WRAP_RENDERER_FACTORY2, (v: RendererFactory2) => v); - rendererFactory = wrapper(ngModule.injector.get(RendererFactory2)) as RendererFactory3; + rendererFactory = ngModule.injector.get(RendererFactory2) as RendererFactory3; + sanitizer = ngModule.injector.get(Sanitizer, null); } else { rendererFactory = domRendererFactory3; } @@ -151,8 +147,8 @@ export class ComponentFactory extends viewEngine_ComponentFactory { // Create the root view. Uses empty TView and ContentTemplate. const rootView: LViewData = createLViewData( - null, renderer, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, - undefined, rootViewInjector); + null, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, + rendererFactory, renderer, sanitizer, rootViewInjector); // rootView is the parent when bootstrapping const oldView = enterView(rootView, null); @@ -162,8 +158,8 @@ export class ComponentFactory extends viewEngine_ComponentFactory { try { if (rendererFactory.begin) rendererFactory.begin(); - const componentView = - createRootComponentView(hostRNode, this.componentDef, rootView, renderer); + const componentView = createRootComponentView( + hostRNode, this.componentDef, rootView, rendererFactory, renderer); tElementNode = getTNode(0, rootView) as TElementNode; // Transform the arrays of native nodes into a structure that can be consumed by the diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index c11da7adb5..4a67e09c59 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -7,7 +7,7 @@ */ import './ng_dev_mode'; -import {assertEqual} from './assert'; +import {assertDomNode} from './assert'; import {EMPTY_ARRAY} from './definition'; import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context'; import {TNode, TNodeFlags} from './interfaces/node'; @@ -95,7 +95,7 @@ export function getContext(target: any): LContext|null { } } else { const rElement = target as RElement; - ngDevMode && assertDomElement(rElement); + ngDevMode && assertDomNode(rElement); // if the context is not found then we need to traverse upwards up the DOM // to find the nearest element that has already been monkey patched with data @@ -262,12 +262,6 @@ function findViaDirective(lViewData: LViewData, directiveInstance: {}): number { return -1; } -function assertDomElement(element: any) { - assertEqual( - element && (element.nodeType == Node.ELEMENT_NODE || element.nodeType == Node.TEXT_NODE), - true, 'The provided value must be an instance of an HTMLElement'); -} - /** * Returns a list of directives extracted from the given view based on the * provided list of directive index values. diff --git a/packages/core/src/render3/debug.ts b/packages/core/src/render3/debug.ts deleted file mode 100644 index 4fadccd52c..0000000000 --- a/packages/core/src/render3/debug.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Injector} from '../di/injector'; -import {Renderer2, RendererType2} from '../render/api'; -import {DebugContext} from '../view'; -import {DebugRenderer2, DebugRendererFactory2} from '../view/services'; - -import {getComponent, getInjector, getLocalRefs, loadContext} from './discovery_utils'; -import {DirectiveDef} from './interfaces/definition'; -import {TNode, TNodeFlags} from './interfaces/node'; -import {TVIEW} from './interfaces/view'; - - -/** - * Adapts the DebugRendererFactory2 to create a DebugRenderer2 specific for IVY. - * - * The created DebugRenderer know how to create a Debug Context specific to IVY. - */ -export class Render3DebugRendererFactory2 extends DebugRendererFactory2 { - createRenderer(element: any, renderData: RendererType2|null): Renderer2 { - const renderer = super.createRenderer(element, renderData) as DebugRenderer2; - renderer.debugContextFactory = (nativeElement: any) => new Render3DebugContext(nativeElement); - return renderer; - } -} - -/** - * Stores context information about view nodes. - * - * Used in tests to retrieve information those nodes. - */ -class Render3DebugContext implements DebugContext { - constructor(private _nativeNode: any) {} - - get nodeIndex(): number|null { return loadContext(this._nativeNode).nodeIndex; } - - get view(): any { return loadContext(this._nativeNode).lViewData; } - - get injector(): Injector { return getInjector(this._nativeNode); } - - get component(): any { return getComponent(this._nativeNode); } - - get providerTokens(): any[] { - const lDebugCtx = loadContext(this._nativeNode); - const lViewData = lDebugCtx.lViewData; - const tNode = lViewData[TVIEW].data[lDebugCtx.nodeIndex] as TNode; - const directivesCount = tNode.flags & TNodeFlags.DirectiveCountMask; - - if (directivesCount > 0) { - const directiveIdxStart = tNode.flags >> TNodeFlags.DirectiveStartingIndexShift; - const directiveIdxEnd = directiveIdxStart + directivesCount; - const viewDirectiveDefs = this.view[TVIEW].data; - const directiveDefs = - viewDirectiveDefs.slice(directiveIdxStart, directiveIdxEnd) as DirectiveDef[]; - - return directiveDefs.map(directiveDef => directiveDef.type); - } - - return []; - } - - get references(): {[key: string]: any} { return getLocalRefs(this._nativeNode); } - - // TODO(pk): check previous implementation and re-implement - get context(): any { throw new Error('Not implemented in ivy'); } - - // TODO(pk): check previous implementation and re-implement - get componentRenderElement(): any { throw new Error('Not implemented in ivy'); } - - // TODO(pk): check previous implementation and re-implement - get renderNode(): any { throw new Error('Not implemented in ivy'); } - - // TODO(pk): check previous implementation and re-implement - logError(console: Console, ...values: any[]): void { console.error(...values); } -} diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 22cb779edb..0c4fe6e4b2 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -20,7 +20,7 @@ import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInject import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType} from './interfaces/node'; import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LViewData, TData, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes} from './node_assert'; -import {getPreviousOrParentTNode, getViewData, setTNodeAndViewData} from './state'; +import {_getViewData, getPreviousOrParentTNode, getViewData, setTNodeAndViewData} from './state'; import {getParentInjectorIndex, getParentInjectorView, hasParentInjector, isComponent, stringify} from './util'; /** @@ -556,13 +556,19 @@ export class NodeInjector implements Injector { constructor( private _tNode: TElementNode|TContainerNode|TElementContainerNode, - private _hostView: LViewData) { - this._injectorIndex = getOrCreateNodeInjectorForNode(_tNode, _hostView); + private _lView: LViewData) { + this._injectorIndex = getOrCreateNodeInjectorForNode(_tNode, _lView); } get(token: any): any { - setTNodeAndViewData(this._tNode, this._hostView); - return getOrCreateInjectable(this._tNode, this._hostView, token); + const previousTNode = getPreviousOrParentTNode(); + const previousLView = _getViewData(); + setTNodeAndViewData(this._tNode, this._lView); + try { + return getOrCreateInjectable(this._tNode, this._lView, token); + } finally { + setTNodeAndViewData(previousTNode, previousLView); + } } } diff --git a/packages/core/src/render3/discovery_utils.ts b/packages/core/src/render3/discovery_utils.ts index eb75cc4f8a..306eb36b5b 100644 --- a/packages/core/src/render3/discovery_utils.ts +++ b/packages/core/src/render3/discovery_utils.ts @@ -141,9 +141,11 @@ export function getDirectives(target: {}): Array<{}> { * Throws if a given target doesn't have associated LContext. * */ -export function loadContext(target: {}): LContext { +export function loadContext(target: {}): LContext; +export function loadContext(target: {}, throwOnNotFound: false): LContext|null; +export function loadContext(target: {}, throwOnNotFound: boolean = true): LContext|null { const context = getContext(target); - if (!context) { + if (!context && throwOnNotFound) { throw new Error( ngDevMode ? `Unable to find context associated with ${stringify(target)}` : 'Invalid ng target'); diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index b77fbf425f..db24c29330 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -13,7 +13,7 @@ import {NgOnChangesFeature} from './features/ng_onchanges_feature'; import {ProvidersFeature} from './features/providers_feature'; import {BaseDef, ComponentDef, ComponentDefWithMeta, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveDefWithMeta, DirectiveType, PipeDef, PipeDefWithMeta} from './interfaces/definition'; -export {ComponentFactory, ComponentFactoryResolver, ComponentRef, WRAP_RENDERER_FACTORY2, injectComponentFactoryResolver} from './component_ref'; +export {ComponentFactory, ComponentFactoryResolver, ComponentRef, injectComponentFactoryResolver} from './component_ref'; export {getFactoryOf, getInheritedFactory} from './di'; export {RenderFlags} from './interfaces/definition'; export {CssSelectorList} from './interfaces/projection'; diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 8a99db8f49..15ecda3a37 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -32,11 +32,11 @@ import {LQueries} from './interfaces/query'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer'; import {SanitizerFn} from './interfaces/sanitization'; import {StylingIndex} from './interfaces/styling'; -import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view'; +import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {appendChild, appendProjectedNode, createTextNode, findComponentView, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; -import {assertDataInRange, assertHasParent, assertPreviousIsParent, decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getCleanup, getContextViewData, getCreationMode, getCurrentQueries, getCurrentSanitizer, getElementDepthCount, getFirstTemplatePass, getIsParent, getPreviousOrParentTNode, getRenderer, getRendererFactory, getTView, getTViewCleanup, getViewData, increaseElementDepthCount, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentQueries, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode, setRenderer, setRendererFactory} from './state'; +import {assertDataInRange, assertHasParent, assertPreviousIsParent, decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getCleanup, getContextViewData, getCreationMode, getCurrentQueries, getCurrentSanitizer, getElementDepthCount, getFirstTemplatePass, getIsParent, getPreviousOrParentTNode, getRenderer, getRendererFactory, getTView, getTViewCleanup, getViewData, increaseElementDepthCount, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentQueries, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state'; import {createStylingContextTemplate, renderStyleAndClassBindings, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings'; import {BoundPlayerFactory} from './styling/player_factory'; import {getStylingContext} from './styling/util'; @@ -155,17 +155,20 @@ function refreshChildComponents(components: number[] | null, rf: RenderFlags | n } export function createLViewData( - parentViewData: LViewData | null, renderer: Renderer3, tView: TView, context: T | null, - flags: LViewFlags, sanitizer?: Sanitizer | null, injector?: Injector | null): LViewData { - const instance = tView.blueprint.slice() as LViewData; - instance[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit; - instance[PARENT] = instance[DECLARATION_VIEW] = parentViewData; - instance[CONTEXT] = context; - instance[INJECTOR as any] = - injector === undefined ? (parentViewData ? parentViewData[INJECTOR] : null) : injector; - instance[RENDERER] = renderer; - instance[SANITIZER] = sanitizer || null; - return instance; + parentLView: LViewData | null, tView: TView, context: T | null, flags: LViewFlags, + rendererFactory?: RendererFactory3 | null, renderer?: Renderer3 | null, + sanitizer?: Sanitizer | null, injector?: Injector | null): LViewData { + const lView = tView.blueprint.slice() as LViewData; + lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.RunInit; + lView[PARENT] = lView[DECLARATION_VIEW] = parentLView; + lView[CONTEXT] = context; + lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY]) !; + ngDevMode && assertDefined(lView[RENDERER_FACTORY], 'RendererFactory is required'); + lView[RENDERER] = (renderer || parentLView && parentLView[RENDERER]) !; + ngDevMode && assertDefined(lView[RENDERER], 'Renderer is required'); + lView[SANITIZER] = sanitizer || parentLView && parentLView[SANITIZER] || null !; + lView[INJECTOR as any] = injector || parentLView && parentLView[INJECTOR] || null; + return lView; } /** @@ -285,20 +288,19 @@ export function renderTemplate( sanitizer?: Sanitizer | null): LViewData { if (hostView == null) { resetComponentState(); - setRendererFactory(providedRendererFactory); const renderer = providedRendererFactory.createRenderer(null, null); - setRenderer(renderer); // We need to create a root view so it's possible to look up the host element through its index - const lView = createLViewData( - null, renderer, createTView(-1, null, 1, 0, null, null, null), {}, - LViewFlags.CheckAlways | LViewFlags.IsRoot); - enterView(lView, null); + const hostLView = createLViewData( + null, createTView(-1, null, 1, 0, null, null, null), {}, + LViewFlags.CheckAlways | LViewFlags.IsRoot, providedRendererFactory, renderer); + enterView(hostLView, null); // SUSPECT! why do we need to enter the View? const componentTView = getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null); hostView = createLViewData( - lView, renderer, componentTView, context, LViewFlags.CheckAlways, sanitizer); + hostLView, componentTView, context, LViewFlags.CheckAlways, providedRendererFactory, + renderer, sanitizer); hostView[HOST_NODE] = createNodeAtIndex(0, TNodeType.Element, hostNode, null, null); } renderComponentOrTemplate(hostView, context, null, templateFn); @@ -319,8 +321,7 @@ export function createEmbeddedViewAndNode( setIsParent(true); setPreviousOrParentTNode(null !); - const lView = createLViewData( - declarationView, renderer, tView, context, LViewFlags.CheckAlways, getCurrentSanitizer()); + const lView = createLViewData(declarationView, tView, context, LViewFlags.CheckAlways); lView[DECLARATION_VIEW] = declarationView; if (queries) { @@ -403,7 +404,7 @@ export function nextContext(level: number = 1): T { function renderComponentOrTemplate( hostView: LViewData, componentOrContext: T, rf: RenderFlags | null, templateFn?: ComponentTemplate) { - const rendererFactory = getRendererFactory(); + const rendererFactory = hostView[RENDERER_FACTORY]; const oldView = enterView(hostView, hostView[HOST_NODE]); try { if (rendererFactory.begin) { @@ -765,7 +766,6 @@ export function createError(text: string, token: any) { export function locateHostElement( factory: RendererFactory3, elementOrSelector: RElement | string): RElement|null { ngDevMode && assertDataInRange(-1); - setRendererFactory(factory); const defaultRenderer = factory.createRenderer(null, null); const rNode = typeof elementOrSelector === 'string' ? (isProceduralRenderer(defaultRenderer) ? @@ -1671,11 +1671,12 @@ function addComponentLogic( // Only component views should be added to the view tree directly. Embedded views are // accessed through their containers because they may be removed / re-added later. + const rendererFactory = getRendererFactory(); const componentView = addToViewTree( viewData, previousOrParentTNode.index as number, createLViewData( - getViewData(), getRendererFactory().createRenderer(native as RElement, def), tView, null, - def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, getCurrentSanitizer())); + getViewData(), tView, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, + rendererFactory, getRendererFactory().createRenderer(native as RElement, def))); componentView[HOST_NODE] = previousOrParentTNode as TElementNode; @@ -2002,9 +2003,9 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num } else { // When we create a new LView, we always reset the state of the instructions. viewToRender = createLViewData( - getViewData(), getRenderer(), + getViewData(), getOrCreateEmbeddedTView(viewBlockId, consts, vars, containerTNode as TContainerNode), null, - LViewFlags.CheckAlways, getCurrentSanitizer()); + LViewFlags.CheckAlways); if (lContainer[QUERIES]) { viewToRender[QUERIES] = lContainer[QUERIES] !.createView(); diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index f578b3b736..7a8f11a64d 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -17,13 +17,10 @@ import {I18nUpdateOpCodes, TI18n} from './i18n'; import {TElementNode, TNode, TViewNode} from './node'; import {PlayerHandler} from './player'; import {LQueries} from './query'; -import {RElement, Renderer3} from './renderer'; +import {RElement, Renderer3, RendererFactory3} from './renderer'; import {StylingContext} from './styling'; -/** Size of LViewData's header. Necessary to adjust for it when setting slots. */ -export const HEADER_OFFSET = 17; - // Below are constants for LViewData indices to help us look up LViewData members // without having to remember the specific indices. // Uglify will inline these when minifying so there shouldn't be a cost. @@ -33,17 +30,21 @@ export const PARENT = 2; export const NEXT = 3; export const QUERIES = 4; export const HOST = 5; -export const HOST_NODE = 6; +export const HOST_NODE = 6; // Rename to `T_HOST`? export const BINDING_INDEX = 7; export const CLEANUP = 8; export const CONTEXT = 9; export const INJECTOR = 10; -export const RENDERER = 11; -export const SANITIZER = 12; -export const TAIL = 13; -export const CONTAINER_INDEX = 14; -export const CONTENT_QUERIES = 15; -export const DECLARATION_VIEW = 16; +export const RENDERER_FACTORY = 11; +export const RENDERER = 12; +export const SANITIZER = 13; +export const TAIL = 14; +export const CONTAINER_INDEX = 15; +export const CONTENT_QUERIES = 16; +export const DECLARATION_VIEW = 17; +/** Size of LViewData's header. Necessary to adjust for it when setting slots. */ +export const HEADER_OFFSET = 18; + // This interface replaces the real LViewData interface if it is an arg or a // return value of a public instruction. This ensures we don't need to expose @@ -149,6 +150,9 @@ export interface LViewData extends Array { /** An optional Module Injector to be used as fall back after Element Injectors are consulted. */ readonly[INJECTOR]: Injector|null; + /** Renderer to be used for this view. */ + [RENDERER_FACTORY]: RendererFactory3; + /** Renderer to be used for this view. */ [RENDERER]: Renderer3; diff --git a/packages/core/src/render3/jit/module.ts b/packages/core/src/render3/jit/module.ts index 601bcbdfc5..1bc4c95be3 100644 --- a/packages/core/src/render3/jit/module.ts +++ b/packages/core/src/render3/jit/module.ts @@ -8,6 +8,7 @@ import {ModuleWithProviders, NgModule, NgModuleDef, NgModuleTransitiveScopes} from '../../metadata/ng_module'; import {Type} from '../../type'; +import {assertDefined} from '../assert'; import {getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef} from '../definition'; import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_INJECTOR_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from '../fields'; import {ComponentDef} from '../interfaces/definition'; @@ -32,6 +33,8 @@ export function compileNgModule(moduleType: Type, ngModule: NgModule = {}): * Compiles and adds the `ngModuleDef` and `ngInjectorDef` properties to the module class. */ export function compileNgModuleDefs(moduleType: Type, ngModule: NgModule): void { + ngDevMode && assertDefined(moduleType, 'Required value moduleType'); + ngDevMode && assertDefined(ngModule, 'Required value ngModule'); const declarations: Type[] = flatten(ngModule.declarations || EMPTY_ARRAY); let ngModuleDef: any = null; diff --git a/packages/core/src/render3/state.ts b/packages/core/src/render3/state.ts index 310e041c04..38417908dd 100644 --- a/packages/core/src/render3/state.ts +++ b/packages/core/src/render3/state.ts @@ -13,7 +13,7 @@ import {executeHooks} from './hooks'; import {TElementNode, TNode, TNodeFlags, TViewNode} from './interfaces/node'; import {LQueries} from './interfaces/query'; import {Renderer3, RendererFactory3} from './interfaces/renderer'; -import {BINDING_INDEX, CLEANUP, CONTEXT, DECLARATION_VIEW, FLAGS, HOST_NODE, LViewData, LViewFlags, OpaqueViewState, QUERIES, RENDERER, SANITIZER, TVIEW, TView} from './interfaces/view'; +import {BINDING_INDEX, CLEANUP, CONTEXT, DECLARATION_VIEW, FLAGS, HOST_NODE, LViewData, LViewFlags, OpaqueViewState, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TVIEW, TView} from './interfaces/view'; import {assertDataInRangeInternal, isContentQueryHost} from './util'; /** @@ -51,10 +51,6 @@ export function getRendererFactory(): RendererFactory3 { return rendererFactory; } -export function setRendererFactory(factory: RendererFactory3): void { - rendererFactory = factory; -} - export function getCurrentSanitizer(): Sanitizer|null { return viewData && viewData[SANITIZER]; } @@ -357,6 +353,7 @@ export function enterView( creationMode = newView && (newView[FLAGS] & LViewFlags.CreationMode) === LViewFlags.CreationMode; firstTemplatePass = newView && tView.firstTemplatePass; bindingRootIndex = newView && tView.bindingStartIndex; + rendererFactory = newView && newView[RENDERER_FACTORY]; renderer = newView && newView[RENDERER]; previousOrParentTNode = hostTNode !; diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index 94ff58ad9c..4ef3d0f2f1 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -13,7 +13,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEn import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, markViewDirty, storeCleanupFn, viewAttached} from './instructions'; import {TNode, TNodeType, TViewNode} from './interfaces/node'; -import {FLAGS, HOST, HOST_NODE, LViewData, LViewFlags, PARENT} from './interfaces/view'; +import {FLAGS, HOST, HOST_NODE, LViewData, LViewFlags, PARENT, RENDERER_FACTORY} from './interfaces/view'; import {destroyLView} from './node_manipulation'; import {getRendererFactory} from './state'; import {getNativeByTNode} from './util'; @@ -241,7 +241,7 @@ export class ViewRef implements viewEngine_EmbeddedViewRef, viewEngine_Int * See {@link ChangeDetectorRef#detach detach} for more information. */ detectChanges(): void { - const rendererFactory = getRendererFactory(); + const rendererFactory = this._view[RENDERER_FACTORY]; if (rendererFactory.begin) { rendererFactory.begin(); } diff --git a/packages/core/src/sanitization/bypass.ts b/packages/core/src/sanitization/bypass.ts index a8b812e225..62125fbdb8 100644 --- a/packages/core/src/sanitization/bypass.ts +++ b/packages/core/src/sanitization/bypass.ts @@ -60,7 +60,7 @@ export interface TrustedUrlString extends TrustedString { [BRAND]: BypassType.Ur export interface TrustedResourceUrlString extends TrustedString { [BRAND]: BypassType.ResourceUrl; } export function allowSanitizationBypass(value: any, type: BypassType): boolean { - return (value instanceof String && (value as TrustedStyleString)[BRAND] === type) ? true : false; + return (value instanceof String && (value as TrustedStyleString)[BRAND] === type); } /** diff --git a/packages/core/src/view/services.ts b/packages/core/src/view/services.ts index 93bfda1741..53e93f6b6d 100644 --- a/packages/core/src/view/services.ts +++ b/packages/core/src/view/services.ts @@ -6,13 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node'; +import {DebugElement__PRE_R3__, DebugNode__PRE_R3__, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node'; import {Injector} from '../di'; import {InjectableDef, getInjectableDef} from '../di/defs'; import {InjectableType} from '../di/injectable'; import {ErrorHandler} from '../error_handler'; import {isDevMode} from '../is_dev_mode'; -import {ivyEnabled} from '../ivy_switch'; import {ComponentFactory} from '../linker/component_factory'; import {NgModuleRef} from '../linker/ng_module_factory'; import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api'; @@ -700,8 +699,8 @@ export class DebugRenderer2 implements Renderer2 { const el = this.delegate.createElement(name, namespace); const debugCtx = this.createDebugContext(el); if (debugCtx) { - const debugEl = new DebugElement(el, null, debugCtx); - debugEl.name = name; + const debugEl = new DebugElement__PRE_R3__(el, null, debugCtx); + (debugEl as{name: string}).name = name; indexDebugNode(debugEl); } return el; @@ -711,7 +710,7 @@ export class DebugRenderer2 implements Renderer2 { const comment = this.delegate.createComment(value); const debugCtx = this.createDebugContext(comment); if (debugCtx) { - indexDebugNode(new DebugNode(comment, null, debugCtx)); + indexDebugNode(new DebugNode__PRE_R3__(comment, null, debugCtx)); } return comment; } @@ -720,7 +719,7 @@ export class DebugRenderer2 implements Renderer2 { const text = this.delegate.createText(value); const debugCtx = this.createDebugContext(text); if (debugCtx) { - indexDebugNode(new DebugNode(text, null, debugCtx)); + indexDebugNode(new DebugNode__PRE_R3__(text, null, debugCtx)); } return text; } @@ -728,7 +727,7 @@ export class DebugRenderer2 implements Renderer2 { appendChild(parent: any, newChild: any): void { const debugEl = getDebugNode(parent); const debugChildEl = getDebugNode(newChild); - if (debugEl && debugChildEl && debugEl instanceof DebugElement) { + if (debugEl && debugChildEl && debugEl instanceof DebugElement__PRE_R3__) { debugEl.addChild(debugChildEl); } this.delegate.appendChild(parent, newChild); @@ -738,7 +737,7 @@ export class DebugRenderer2 implements Renderer2 { const debugEl = getDebugNode(parent); const debugChildEl = getDebugNode(newChild); const debugRefEl = getDebugNode(refChild) !; - if (debugEl && debugChildEl && debugEl instanceof DebugElement) { + if (debugEl && debugChildEl && debugEl instanceof DebugElement__PRE_R3__) { debugEl.insertBefore(debugRefEl, debugChildEl); } @@ -748,7 +747,7 @@ export class DebugRenderer2 implements Renderer2 { removeChild(parent: any, oldChild: any): void { const debugEl = getDebugNode(parent); const debugChildEl = getDebugNode(oldChild); - if (debugEl && debugChildEl && debugEl instanceof DebugElement) { + if (debugEl && debugChildEl && debugEl instanceof DebugElement__PRE_R3__) { debugEl.removeChild(debugChildEl); } this.delegate.removeChild(parent, oldChild); @@ -756,16 +755,16 @@ export class DebugRenderer2 implements Renderer2 { selectRootElement(selectorOrNode: string|any, preserveContent?: boolean): any { const el = this.delegate.selectRootElement(selectorOrNode, preserveContent); - const debugCtx = getCurrentDebugContext() || (ivyEnabled ? this.createDebugContext(el) : null); + const debugCtx = getCurrentDebugContext(); if (debugCtx) { - indexDebugNode(new DebugElement(el, null, debugCtx)); + indexDebugNode(new DebugElement__PRE_R3__(el, null, debugCtx)); } return el; } setAttribute(el: any, name: string, value: string, namespace?: string): void { const debugEl = getDebugNode(el); - if (debugEl && debugEl instanceof DebugElement) { + if (debugEl && debugEl instanceof DebugElement__PRE_R3__) { const fullName = namespace ? namespace + ':' + name : name; debugEl.attributes[fullName] = value; } @@ -774,7 +773,7 @@ export class DebugRenderer2 implements Renderer2 { removeAttribute(el: any, name: string, namespace?: string): void { const debugEl = getDebugNode(el); - if (debugEl && debugEl instanceof DebugElement) { + if (debugEl && debugEl instanceof DebugElement__PRE_R3__) { const fullName = namespace ? namespace + ':' + name : name; debugEl.attributes[fullName] = null; } @@ -783,7 +782,7 @@ export class DebugRenderer2 implements Renderer2 { addClass(el: any, name: string): void { const debugEl = getDebugNode(el); - if (debugEl && debugEl instanceof DebugElement) { + if (debugEl && debugEl instanceof DebugElement__PRE_R3__) { debugEl.classes[name] = true; } this.delegate.addClass(el, name); @@ -791,7 +790,7 @@ export class DebugRenderer2 implements Renderer2 { removeClass(el: any, name: string): void { const debugEl = getDebugNode(el); - if (debugEl && debugEl instanceof DebugElement) { + if (debugEl && debugEl instanceof DebugElement__PRE_R3__) { debugEl.classes[name] = false; } this.delegate.removeClass(el, name); @@ -799,7 +798,7 @@ export class DebugRenderer2 implements Renderer2 { setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void { const debugEl = getDebugNode(el); - if (debugEl && debugEl instanceof DebugElement) { + if (debugEl && debugEl instanceof DebugElement__PRE_R3__) { debugEl.styles[style] = value; } this.delegate.setStyle(el, style, value, flags); @@ -807,7 +806,7 @@ export class DebugRenderer2 implements Renderer2 { removeStyle(el: any, style: string, flags: RendererStyleFlags2): void { const debugEl = getDebugNode(el); - if (debugEl && debugEl instanceof DebugElement) { + if (debugEl && debugEl instanceof DebugElement__PRE_R3__) { debugEl.styles[style] = null; } this.delegate.removeStyle(el, style, flags); @@ -815,7 +814,7 @@ export class DebugRenderer2 implements Renderer2 { setProperty(el: any, name: string, value: any): void { const debugEl = getDebugNode(el); - if (debugEl && debugEl instanceof DebugElement) { + if (debugEl && debugEl instanceof DebugElement__PRE_R3__) { debugEl.properties[name] = value; } this.delegate.setProperty(el, name, value); diff --git a/packages/core/test/bundling/animation_world/bundle.golden_symbols.json b/packages/core/test/bundling/animation_world/bundle.golden_symbols.json index 0f06d41c67..f707d66980 100644 --- a/packages/core/test/bundling/animation_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animation_world/bundle.golden_symbols.json @@ -176,6 +176,9 @@ { "name": "RENDERER" }, + { + "name": "RENDERER_FACTORY" + }, { "name": "RENDER_PARENT" }, @@ -611,9 +614,6 @@ { "name": "getCurrentQueries" }, - { - "name": "getCurrentSanitizer" - }, { "name": "getDirectiveDef" }, @@ -1145,9 +1145,6 @@ { "name": "setProp" }, - { - "name": "setRendererFactory" - }, { "name": "setStyle" }, diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index de04084e40..f6366eefae 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -107,6 +107,9 @@ { "name": "RENDERER" }, + { + "name": "RENDERER_FACTORY" + }, { "name": "RENDER_PARENT" }, @@ -320,9 +323,6 @@ { "name": "getRenderer" }, - { - "name": "getRendererFactory" - }, { "name": "getRootContext" }, @@ -458,9 +458,6 @@ { "name": "setPreviousOrParentTNode" }, - { - "name": "setRendererFactory" - }, { "name": "setTNodeAndViewData" }, diff --git a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json index 18a8ae65f2..cb0033c3f4 100644 --- a/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world_r2/bundle.golden_symbols.json @@ -356,6 +356,9 @@ { "name": "RENDERER" }, + { + "name": "RENDERER_FACTORY" + }, { "name": "RENDER_PARENT" }, @@ -392,6 +395,9 @@ { "name": "SafeSubscriber" }, + { + "name": "Sanitizer" + }, { "name": "Self" }, @@ -455,9 +461,6 @@ { "name": "ViewEncapsulation" }, - { - "name": "WRAP_RENDERER_FACTORY2" - }, { "name": "_CLEAN_PROMISE" }, @@ -548,6 +551,9 @@ { "name": "_enable_super_gross_mode_that_will_cause_bad_things" }, + { + "name": "_getViewData" + }, { "name": "_global" }, @@ -950,9 +956,6 @@ { "name": "getRenderer" }, - { - "name": "getRendererFactory" - }, { "name": "getRootContext" }, @@ -1322,9 +1325,6 @@ { "name": "setPreviousOrParentTNode" }, - { - "name": "setRendererFactory" - }, { "name": "setRootDomAdapter" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 9c566d2385..1cd1279cf1 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -170,6 +170,9 @@ { "name": "RENDERER" }, + { + "name": "RENDERER_FACTORY" + }, { "name": "RENDER_PARENT" }, @@ -659,9 +662,6 @@ { "name": "getCurrentQueries" }, - { - "name": "getCurrentSanitizer" - }, { "name": "getCurrentView" }, @@ -1166,9 +1166,6 @@ { "name": "setProp" }, - { - "name": "setRendererFactory" - }, { "name": "setStyle" }, diff --git a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json index b5f77a965d..3bfaafe7f9 100644 --- a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json @@ -137,6 +137,24 @@ { "name": "Compiler" }, + { + "name": "Compiler_compileModuleAndAllComponentsAsync" + }, + { + "name": "Compiler_compileModuleAndAllComponentsSync" + }, + { + "name": "Compiler_compileModuleAndAllComponentsSync__POST_R3__" + }, + { + "name": "Compiler_compileModuleAsync" + }, + { + "name": "Compiler_compileModuleSync" + }, + { + "name": "Compiler_compileModuleSync__POST_R3__" + }, { "name": "ComponentFactory" }, @@ -209,6 +227,12 @@ { "name": "DateType" }, + { + "name": "DebugElement__POST_R3__" + }, + { + "name": "DebugNode__POST_R3__" + }, { "name": "DecimalPipe" }, @@ -446,6 +470,9 @@ { "name": "MergeMapSubscriber" }, + { + "name": "ModuleWithComponentFactories" + }, { "name": "MulticastOperator" }, @@ -698,6 +725,9 @@ { "name": "RENDERER" }, + { + "name": "RENDERER_FACTORY" + }, { "name": "RENDER_PARENT" }, @@ -950,9 +980,6 @@ { "name": "ViewRef" }, - { - "name": "WRAP_RENDERER_FACTORY2" - }, { "name": "WrappedValue" }, @@ -1136,6 +1163,9 @@ { "name": "_enable_super_gross_mode_that_will_cause_bad_things" }, + { + "name": "_getViewData" + }, { "name": "_global" }, @@ -1151,9 +1181,6 @@ { "name": "_localeFactory" }, - { - "name": "_nativeNodeToDebugNode" - }, { "name": "_ngProbeTokensToMap" }, @@ -1163,6 +1190,9 @@ { "name": "_promiseStrategy" }, + { + "name": "_queryNodeChildrenR3" + }, { "name": "_randomChar" }, @@ -1184,9 +1214,6 @@ { "name": "_testabilityGetter" }, - { - "name": "_throwError" - }, { "name": "addComponentLogic" }, @@ -1481,6 +1508,9 @@ { "name": "directiveInject" }, + { + "name": "discoverLocalRefs" + }, { "name": "domRendererFactory3" }, @@ -1565,6 +1595,12 @@ { "name": "findViaComponent" }, + { + "name": "findViaDirective" + }, + { + "name": "findViaNativeElement" + }, { "name": "firstTemplatePass" }, @@ -1646,6 +1682,12 @@ { "name": "getClosureSafeProperty" }, + { + "name": "getComponent" + }, + { + "name": "getComponentAtNodeIndex" + }, { "name": "getComponentDef" }, @@ -1658,6 +1700,9 @@ { "name": "getContainerRenderParent" }, + { + "name": "getContext" + }, { "name": "getContextViewData" }, @@ -1670,9 +1715,6 @@ { "name": "getCurrentQueries" }, - { - "name": "getCurrentSanitizer" - }, { "name": "getCurrentView" }, @@ -1694,9 +1736,21 @@ { "name": "getDebugNode" }, + { + "name": "getDebugNode__POST_R3__" + }, { "name": "getDirectiveDef" }, + { + "name": "getDirectiveEndIndex" + }, + { + "name": "getDirectiveStartIndex" + }, + { + "name": "getDirectivesAtNodeIndex" + }, { "name": "getElementDepthCount" }, @@ -1727,6 +1781,9 @@ { "name": "getInjectableDef" }, + { + "name": "getInjector" + }, { "name": "getInjectorDef" }, @@ -1745,6 +1802,9 @@ { "name": "getLastDefinedValue" }, + { + "name": "getLocalRefs" + }, { "name": "getLocaleCurrencies" }, @@ -2075,6 +2135,9 @@ { "name": "isComponentDef" }, + { + "name": "isComponentInstance" + }, { "name": "isContextDirty" }, @@ -2093,6 +2156,12 @@ { "name": "isDifferent" }, + { + "name": "isDirectiveDefHack" + }, + { + "name": "isDirectiveInstance" + }, { "name": "isDirty" }, @@ -2195,6 +2264,9 @@ { "name": "listener" }, + { + "name": "loadContext" + }, { "name": "loadInternal" }, @@ -2288,6 +2360,9 @@ { "name": "noopScope" }, + { + "name": "notImplemented" + }, { "name": "observable" }, @@ -2531,9 +2606,6 @@ { "name": "setProp" }, - { - "name": "setRendererFactory" - }, { "name": "setRootDomAdapter" }, @@ -2660,6 +2732,9 @@ { "name": "trackByIdentity" }, + { + "name": "traverseNextElement" + }, { "name": "tryCatch" }, diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts index 6f9dfb96f4..24780ee87c 100644 --- a/packages/core/test/linker/integration_spec.ts +++ b/packages/core/test/linker/integration_spec.ts @@ -940,24 +940,26 @@ function declareTests(config?: {useJit: boolean}) { expect(getDOM().getProperty(tc.nativeElement, 'id')).toEqual('newId'); }); - it('should not use template variables for expressions in hostProperties', () => { - @Directive({selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}}) - class DirectiveWithHostProps { - id = 'one'; - } + fixmeIvy('FW-681: not possible to retrieve host property bindings from TView') && + it('should not use template variables for expressions in hostProperties', () => { + @Directive( + {selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}}) + class DirectiveWithHostProps { + id = 'one'; + } - const fixture = - TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]}) - .overrideComponent( - MyComp, - {set: {template: `
`}}) - .createComponent(MyComp); - fixture.detectChanges(); + const fixture = + TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]}) + .overrideComponent(MyComp, { + set: {template: `
`} + }) + .createComponent(MyComp); + fixture.detectChanges(); - const tc = fixture.debugElement.children[0]; - expect(tc.properties['id']).toBe('one'); - expect(tc.properties['title']).toBe(undefined); - }); + const tc = fixture.debugElement.children[0]; + expect(tc.properties['id']).toBe('one'); + expect(tc.properties['title']).toBe(undefined); + }); fixmeIvy('FW-725: Pipes in host bindings fail with a cryptic error') && it('should not allow pipes in hostProperties', () => { @@ -1113,7 +1115,7 @@ function declareTests(config?: {useJit: boolean}) { .toHaveText('dynamic greet'); })); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-707: TestBed: No LViewData in getParentInjectorLocation') && it('should create a component that has been freshly compiled', () => { @Component({template: ''}) class RootComp { @@ -1153,7 +1155,7 @@ function declareTests(config?: {useJit: boolean}) { expect(compRef.instance.someToken).toBe('someRootValue'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-707: TestBed: No LViewData in getParentInjectorLocation') && it('should create a component with the passed NgModuleRef', () => { @Component({template: ''}) class RootComp { @@ -1194,7 +1196,7 @@ function declareTests(config?: {useJit: boolean}) { expect(compRef.instance.someToken).toBe('someValue'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-707: TestBed: No LViewData in getParentInjectorLocation') && it('should create a component with the NgModuleRef of the ComponentFactoryResolver', () => { @Component({template: ''}) diff --git a/packages/core/test/linker/ng_module_integration_spec.ts b/packages/core/test/linker/ng_module_integration_spec.ts index 4ae6555c26..d6bdaec52b 100644 --- a/packages/core/test/linker/ng_module_integration_spec.ts +++ b/packages/core/test/linker/ng_module_integration_spec.ts @@ -142,8 +142,8 @@ function declareTests(config?: {useJit: boolean}) { return new ComponentFixture(comp, null !, false); } - describe('errors', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && // + describe('errors', () => { it('should error when exporting a directive that was neither declared nor imported', () => { @NgModule({exports: [SomeDirective]}) class SomeModule { @@ -154,7 +154,6 @@ function declareTests(config?: {useJit: boolean}) { `Can't export directive ${stringify(SomeDirective)} from ${stringify(SomeModule)} as it was neither declared nor imported!`); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should error when exporting a pipe that was neither declared nor imported', () => { @NgModule({exports: [SomePipe]}) class SomeModule { @@ -165,7 +164,6 @@ function declareTests(config?: {useJit: boolean}) { `Can't export pipe ${stringify(SomePipe)} from ${stringify(SomeModule)} as it was neither declared nor imported!`); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should error if a directive is declared in more than 1 module', () => { @NgModule({declarations: [SomeDirective]}) class Module1 { @@ -184,7 +182,6 @@ function declareTests(config?: {useJit: boolean}) { `You can also create a new NgModule that exports and includes ${stringify(SomeDirective)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should error if a directive is declared in more than 1 module also if the module declaring it is imported', () => { @NgModule({declarations: [SomeDirective], exports: [SomeDirective]}) @@ -202,7 +199,6 @@ function declareTests(config?: {useJit: boolean}) { `You can also create a new NgModule that exports and includes ${stringify(SomeDirective)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should error if a pipe is declared in more than 1 module', () => { @NgModule({declarations: [SomePipe]}) class Module1 { @@ -221,7 +217,6 @@ function declareTests(config?: {useJit: boolean}) { `You can also create a new NgModule that exports and includes ${stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should error if a pipe is declared in more than 1 module also if the module declaring it is imported', () => { @NgModule({declarations: [SomePipe], exports: [SomePipe]}) @@ -239,10 +234,10 @@ function declareTests(config?: {useJit: boolean}) { `You can also create a new NgModule that exports and includes ${stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`); }); - }); + }); describe('schemas', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should error on unknown bound properties on custom elements by default', () => { @Component({template: ''}) class ComponentUsingInvalidProperty { @@ -255,22 +250,19 @@ function declareTests(config?: {useJit: boolean}) { expect(() => createModule(SomeModule)).toThrowError(/Can't bind to 'someUnknownProp'/); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should not error on unknown bound properties on custom elements when using the CUSTOM_ELEMENTS_SCHEMA', - () => { - @Component({template: ''}) - class ComponentUsingInvalidProperty { - } + it('should not error on unknown bound properties on custom elements when using the CUSTOM_ELEMENTS_SCHEMA', + () => { + @Component({template: ''}) + class ComponentUsingInvalidProperty { + } - @NgModule({ - schemas: [CUSTOM_ELEMENTS_SCHEMA], - declarations: [ComponentUsingInvalidProperty] - }) - class SomeModule { - } + @NgModule( + {schemas: [CUSTOM_ELEMENTS_SCHEMA], declarations: [ComponentUsingInvalidProperty]}) + class SomeModule { + } - expect(() => createModule(SomeModule)).not.toThrow(); - }); + expect(() => createModule(SomeModule)).not.toThrow(); + }); }); describe('id', () => { @@ -284,7 +276,7 @@ function declareTests(config?: {useJit: boolean}) { afterEach(() => clearModulesForTest()); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-740: missing global registry of NgModules by id') && it('should register loaded modules', () => { createModule(SomeModule); const factory = getModuleFactory(token); @@ -292,7 +284,7 @@ function declareTests(config?: {useJit: boolean}) { expect(factory.moduleType).toBe(SomeModule); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should throw when registering a duplicate module', () => { createModule(SomeModule); expect(() => createModule(SomeOtherModule)).toThrowError(/Duplicate module registered/); @@ -300,23 +292,21 @@ function declareTests(config?: {useJit: boolean}) { }); describe('entryComponents', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should create ComponentFactories in root modules', () => { - @NgModule({declarations: [SomeComp], entryComponents: [SomeComp]}) - class SomeModule { - } + it('should create ComponentFactories in root modules', () => { + @NgModule({declarations: [SomeComp], entryComponents: [SomeComp]}) + class SomeModule { + } - const ngModule = createModule(SomeModule); - expect( - ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) - .toBe(SomeComp); - expect(ngModule.injector.get(ComponentFactoryResolver) - .resolveComponentFactory(SomeComp) - .componentType) - .toBe(SomeComp); - }); + const ngModule = createModule(SomeModule); + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + .toBe(SomeComp); + expect(ngModule.injector.get(ComponentFactoryResolver) + .resolveComponentFactory(SomeComp) + .componentType) + .toBe(SomeComp); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should throw if we cannot find a module associated with a module-level entryComponent', () => { @Component({template: ''}) @@ -332,7 +322,7 @@ function declareTests(config?: {useJit: boolean}) { 'Component SomeCompWithEntryComponents is not part of any NgModule or the module has not been imported into your module.'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should throw if we cannot find a module associated with a component-level entryComponent', () => { @Component({template: '', entryComponents: [SomeComp]}) @@ -348,100 +338,91 @@ function declareTests(config?: {useJit: boolean}) { 'Component SomeComp is not part of any NgModule or the module has not been imported into your module.'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should create ComponentFactories via ANALYZE_FOR_ENTRY_COMPONENTS', () => { - @NgModule({ - declarations: [SomeComp], - providers: [{ - provide: ANALYZE_FOR_ENTRY_COMPONENTS, - multi: true, - useValue: [{a: 'b', component: SomeComp}] - }] - }) - class SomeModule { - } + it('should create ComponentFactories via ANALYZE_FOR_ENTRY_COMPONENTS', () => { + @NgModule({ + declarations: [SomeComp], + providers: [{ + provide: ANALYZE_FOR_ENTRY_COMPONENTS, + multi: true, + useValue: [{a: 'b', component: SomeComp}] + }] + }) + class SomeModule { + } - const ngModule = createModule(SomeModule); - expect( - ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) - .toBe(SomeComp); - expect(ngModule.injector.get(ComponentFactoryResolver) - .resolveComponentFactory(SomeComp) - .componentType) - .toBe(SomeComp); - }); + const ngModule = createModule(SomeModule); + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + .toBe(SomeComp); + expect(ngModule.injector.get(ComponentFactoryResolver) + .resolveComponentFactory(SomeComp) + .componentType) + .toBe(SomeComp); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should create ComponentFactories in imported modules', () => { - @NgModule({declarations: [SomeComp], entryComponents: [SomeComp]}) - class SomeImportedModule { - } + it('should create ComponentFactories in imported modules', () => { + @NgModule({declarations: [SomeComp], entryComponents: [SomeComp]}) + class SomeImportedModule { + } - @NgModule({imports: [SomeImportedModule]}) - class SomeModule { - } + @NgModule({imports: [SomeImportedModule]}) + class SomeModule { + } - const ngModule = createModule(SomeModule); - expect( - ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) - .toBe(SomeComp); - expect(ngModule.injector.get(ComponentFactoryResolver) - .resolveComponentFactory(SomeComp) - .componentType) - .toBe(SomeComp); - }); + const ngModule = createModule(SomeModule); + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + .toBe(SomeComp); + expect(ngModule.injector.get(ComponentFactoryResolver) + .resolveComponentFactory(SomeComp) + .componentType) + .toBe(SomeComp); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should create ComponentFactories if the component was imported', () => { - @NgModule({declarations: [SomeComp], exports: [SomeComp]}) - class SomeImportedModule { - } + it('should create ComponentFactories if the component was imported', () => { + @NgModule({declarations: [SomeComp], exports: [SomeComp]}) + class SomeImportedModule { + } - @NgModule({imports: [SomeImportedModule], entryComponents: [SomeComp]}) - class SomeModule { - } + @NgModule({imports: [SomeImportedModule], entryComponents: [SomeComp]}) + class SomeModule { + } - const ngModule = createModule(SomeModule); - expect( - ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) - .toBe(SomeComp); - expect(ngModule.injector.get(ComponentFactoryResolver) - .resolveComponentFactory(SomeComp) - .componentType) - .toBe(SomeComp); - }); + const ngModule = createModule(SomeModule); + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + .toBe(SomeComp); + expect(ngModule.injector.get(ComponentFactoryResolver) + .resolveComponentFactory(SomeComp) + .componentType) + .toBe(SomeComp); + }); }); describe('bootstrap components', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should create ComponentFactories', () => { - @NgModule({declarations: [SomeComp], bootstrap: [SomeComp]}) - class SomeModule { - } + it('should create ComponentFactories', () => { + @NgModule({declarations: [SomeComp], bootstrap: [SomeComp]}) + class SomeModule { + } - const ngModule = createModule(SomeModule); - expect( - ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) - .toBe(SomeComp); - }); + const ngModule = createModule(SomeModule); + expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType) + .toBe(SomeComp); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should store the ComponentFactories in the NgModuleInjector', () => { - @NgModule({declarations: [SomeComp], bootstrap: [SomeComp]}) - class SomeModule { - } + it('should store the ComponentFactories in the NgModuleInjector', () => { + @NgModule({declarations: [SomeComp], bootstrap: [SomeComp]}) + class SomeModule { + } - const ngModule = >createModule(SomeModule); - expect(ngModule._bootstrapComponents.length).toBe(1); - expect(ngModule._bootstrapComponents[0]).toBe(SomeComp); - }); + const ngModule = >createModule(SomeModule); + expect(ngModule._bootstrapComponents.length).toBe(1); + expect(ngModule._bootstrapComponents[0]).toBe(SomeComp); + }); }); describe('directives and pipes', () => { - describe('declarations', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-681: not possible to retrieve host property bindings from TView') && + describe('declarations', () => { it('should be supported in root modules', () => { @NgModule({ declarations: [CompUsingModuleDirectiveAndPipe, SomeDirective, SomePipe], @@ -457,7 +438,6 @@ function declareTests(config?: {useJit: boolean}) { .toBe('transformed someValue'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should be supported in imported modules', () => { @NgModule({ declarations: [CompUsingModuleDirectiveAndPipe, SomeDirective, SomePipe], @@ -477,7 +457,6 @@ function declareTests(config?: {useJit: boolean}) { }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should be supported in nested components', () => { @Component({ selector: 'parent', @@ -501,11 +480,11 @@ function declareTests(config?: {useJit: boolean}) { expect(compFixture.debugElement.children[0].children[0].properties['title']) .toBe('transformed someValue'); }); - }); + }); describe('import/export', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('Pipe with name \'somePipe\' not found!') && it('should support exported directives and pipes', () => { @NgModule( {declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]}) @@ -527,7 +506,7 @@ function declareTests(config?: {useJit: boolean}) { .toBe('transformed someValue'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('Pipe with name \'somePipe\' not found!') && it('should support exported directives and pipes if the module is wrapped into an `ModuleWithProviders`', () => { @NgModule( @@ -550,7 +529,7 @@ function declareTests(config?: {useJit: boolean}) { .toBe('transformed someValue'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('Pipe with name \'somePipe\' not found!') && it('should support reexported modules', () => { @NgModule( {declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]}) @@ -575,7 +554,7 @@ function declareTests(config?: {useJit: boolean}) { .toBe('transformed someValue'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('Pipe with name \'somePipe\' not found!') && it('should support exporting individual directives of an imported module', () => { @NgModule( {declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]}) @@ -600,7 +579,7 @@ function declareTests(config?: {useJit: boolean}) { .toBe('transformed someValue'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should not use non exported pipes of an imported module', () => { @NgModule({ declarations: [SomePipe], @@ -620,7 +599,7 @@ function declareTests(config?: {useJit: boolean}) { .toThrowError(/The pipe 'somePipe' could not be found/); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should not use non exported directives of an imported module', () => { @NgModule({ declarations: [SomeDirective], @@ -657,49 +636,45 @@ function declareTests(config?: {useJit: boolean}) { return createModule(SomeModule, parent).injector; } - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should provide the module', () => { - expect(createInjector([]).get(moduleType)).toBeAnInstanceOf(moduleType); + it('should provide the module', + () => { expect(createInjector([]).get(moduleType)).toBeAnInstanceOf(moduleType); }); + + it('should instantiate a class without dependencies', () => { + const injector = createInjector([Engine]); + const engine = injector.get(Engine); + + expect(engine).toBeAnInstanceOf(Engine); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should instantiate a class without dependencies', () => { - const injector = createInjector([Engine]); - const engine = injector.get(Engine); + it('should resolve dependencies based on type information', () => { + const injector = createInjector([Engine, Car]); + const car = injector.get(Car); - expect(engine).toBeAnInstanceOf(Engine); - }); + expect(car).toBeAnInstanceOf(Car); + expect(car.engine).toBeAnInstanceOf(Engine); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should resolve dependencies based on type information', () => { - const injector = createInjector([Engine, Car]); - const car = injector.get(Car); + it('should resolve dependencies based on @Inject annotation', () => { + const injector = createInjector([TurboEngine, Engine, CarWithInject]); + const car = injector.get(CarWithInject); - expect(car).toBeAnInstanceOf(Car); - expect(car.engine).toBeAnInstanceOf(Engine); - }); + expect(car).toBeAnInstanceOf(CarWithInject); + expect(car.engine).toBeAnInstanceOf(TurboEngine); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should resolve dependencies based on @Inject annotation', () => { - const injector = createInjector([TurboEngine, Engine, CarWithInject]); - const car = injector.get(CarWithInject); - - expect(car).toBeAnInstanceOf(CarWithInject); - expect(car.engine).toBeAnInstanceOf(TurboEngine); - }); - - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should throw when no type and not @Inject (class case)', () => { expect(() => createInjector([NoAnnotations])) .toThrowError('Can\'t resolve all parameters for NoAnnotations: (?).'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should throw when no type and not @Inject (factory case)', () => { expect(() => createInjector([{provide: 'someToken', useFactory: factoryFn}])) .toThrowError('Can\'t resolve all parameters for factoryFn: (?).'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should cache instances', () => { + it('should cache instances', () => { const injector = createInjector([Engine]); const e1 = injector.get(Engine); @@ -708,33 +683,31 @@ function declareTests(config?: {useJit: boolean}) { expect(e1).toBe(e2); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should provide to a value', () => { + it('should provide to a value', () => { const injector = createInjector([{provide: Engine, useValue: 'fake engine'}]); const engine = injector.get(Engine); expect(engine).toEqual('fake engine'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should provide to a factory', () => { - function sportsCarFactory(e: Engine) { return new SportsCar(e); } + it('should provide to a factory', () => { + function sportsCarFactory(e: Engine) { return new SportsCar(e); } - const injector = createInjector( - [Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]); + const injector = + createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]); - const car = injector.get(Car); - expect(car).toBeAnInstanceOf(SportsCar); - expect(car.engine).toBeAnInstanceOf(Engine); - }); + const car = injector.get(Car); + expect(car).toBeAnInstanceOf(SportsCar); + expect(car.engine).toBeAnInstanceOf(Engine); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should supporting provider to null', () => { - const injector = createInjector([{provide: Engine, useValue: null}]); - const engine = injector.get(Engine); - expect(engine).toBeNull(); - }); + it('should supporting provider to null', () => { + const injector = createInjector([{provide: Engine, useValue: null}]); + const engine = injector.get(Engine); + expect(engine).toBeNull(); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should provide to an alias', () => { + it('should provide to an alias', () => { const injector = createInjector([ Engine, {provide: SportsCar, useClass: SportsCar}, {provide: Car, useExisting: SportsCar} @@ -746,109 +719,99 @@ function declareTests(config?: {useJit: boolean}) { expect(car).toBe(sportsCar); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should support multiProviders', () => { - const injector = createInjector([ - Engine, {provide: CARS, useClass: SportsCar, multi: true}, - {provide: CARS, useClass: CarWithOptionalEngine, multi: true} - ]); + it('should support multiProviders', () => { + const injector = createInjector([ + Engine, {provide: CARS, useClass: SportsCar, multi: true}, + {provide: CARS, useClass: CarWithOptionalEngine, multi: true} + ]); - const cars = injector.get(CARS); - expect(cars.length).toEqual(2); - expect(cars[0]).toBeAnInstanceOf(SportsCar); - expect(cars[1]).toBeAnInstanceOf(CarWithOptionalEngine); - }); + const cars = injector.get(CARS); + expect(cars.length).toEqual(2); + expect(cars[0]).toBeAnInstanceOf(SportsCar); + expect(cars[1]).toBeAnInstanceOf(CarWithOptionalEngine); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should support multiProviders that are created using useExisting', () => { - const injector = createInjector( - [Engine, SportsCar, {provide: CARS, useExisting: SportsCar, multi: true}]); + it('should support multiProviders that are created using useExisting', () => { + const injector = createInjector( + [Engine, SportsCar, {provide: CARS, useExisting: SportsCar, multi: true}]); - const cars = injector.get(CARS); - expect(cars.length).toEqual(1); - expect(cars[0]).toBe(injector.get(SportsCar)); - }); + const cars = injector.get(CARS); + expect(cars.length).toEqual(1); + expect(cars[0]).toBe(injector.get(SportsCar)); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should throw when the aliased provider does not exist', () => { const injector = createInjector([{provide: 'car', useExisting: SportsCar}]); const e = `NullInjectorError: No provider for ${stringify(SportsCar)}!`; expect(() => injector.get('car')).toThrowError(e); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should handle forwardRef in useExisting', () => { - const injector = createInjector([ - {provide: 'originalEngine', useClass: forwardRef(() => Engine)}, - {provide: 'aliasedEngine', useExisting: forwardRef(() => 'originalEngine')} - ]); - expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine); - }); + it('should handle forwardRef in useExisting', () => { + const injector = createInjector([ + {provide: 'originalEngine', useClass: forwardRef(() => Engine)}, + {provide: 'aliasedEngine', useExisting: forwardRef(() => 'originalEngine')} + ]); + expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should support overriding factory dependencies', () => { - const injector = createInjector([ - Engine, {provide: Car, useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]} - ]); + it('should support overriding factory dependencies', () => { + const injector = createInjector( + [Engine, {provide: Car, useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]}]); - const car = injector.get(Car); - expect(car).toBeAnInstanceOf(SportsCar); - expect(car.engine).toBeAnInstanceOf(Engine); - }); + const car = injector.get(Car); + expect(car).toBeAnInstanceOf(SportsCar); + expect(car.engine).toBeAnInstanceOf(Engine); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should support optional dependencies', () => { - const injector = createInjector([CarWithOptionalEngine]); + it('should support optional dependencies', () => { + const injector = createInjector([CarWithOptionalEngine]); - const car = injector.get(CarWithOptionalEngine); - expect(car.engine).toBeNull(); - }); + const car = injector.get(CarWithOptionalEngine); + expect(car.engine).toBeNull(); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should flatten passed-in providers', () => { - const injector = createInjector([[[Engine, Car]]]); + it('should flatten passed-in providers', () => { + const injector = createInjector([[[Engine, Car]]]); - const car = injector.get(Car); - expect(car).toBeAnInstanceOf(Car); - }); + const car = injector.get(Car); + expect(car).toBeAnInstanceOf(Car); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should use the last provider when there are multiple providers for same token', - () => { - const injector = createInjector( - [{provide: Engine, useClass: Engine}, {provide: Engine, useClass: TurboEngine}]); + it('should use the last provider when there are multiple providers for same token', () => { + const injector = createInjector( + [{provide: Engine, useClass: Engine}, {provide: Engine, useClass: TurboEngine}]); - expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine); - }); + expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should use non-type tokens', () => { + it('should use non-type tokens', () => { const injector = createInjector([{provide: 'token', useValue: 'value'}]); expect(injector.get('token')).toEqual('value'); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should throw when given invalid providers', () => { expect(() => createInjector(['blah'])) .toThrowError( `Invalid provider for the NgModule 'SomeModule' - only instances of Provider and Type are allowed, got: [?blah?]`); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should throw when given blank providers', () => { - expect(() => createInjector([null, {provide: 'token', useValue: 'value'}])) - .toThrowError( - `Invalid provider for the NgModule 'SomeModule' - only instances of Provider and Type are allowed, got: [?null?, ...]`); - }); + fixmeIvy('FW-682: Compiler error handling') && it('should throw when given blank providers', () => { + expect(() => createInjector([null, {provide: 'token', useValue: 'value'}])) + .toThrowError( + `Invalid provider for the NgModule 'SomeModule' - only instances of Provider and Type are allowed, got: [?null?, ...]`); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should provide itself', () => { + it('should provide itself', () => { const parent = createInjector([]); const child = createInjector([], parent); expect(child.get(Injector)).toBe(child); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should provide undefined', () => { + it('should provide undefined', () => { let factoryCounter = 0; const injector = createInjector([{ @@ -866,193 +829,180 @@ function declareTests(config?: {useJit: boolean}) { describe('injecting lazy providers into an eager provider via Injector.get', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should inject providers that were declared before it', () => { - @NgModule({ - providers: [ - {provide: 'lazy', useFactory: () => 'lazyValue'}, - { - provide: 'eager', - useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`, - deps: [Injector] - }, - ] - }) - class MyModule { - // NgModule is eager, which makes all of its deps eager - constructor(@Inject('eager') eager: any) {} - } + it('should inject providers that were declared before it', () => { + @NgModule({ + providers: [ + {provide: 'lazy', useFactory: () => 'lazyValue'}, + { + provide: 'eager', + useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`, + deps: [Injector] + }, + ] + }) + class MyModule { + // NgModule is eager, which makes all of its deps eager + constructor(@Inject('eager') eager: any) {} + } - expect(createModule(MyModule).injector.get('eager')).toBe('eagerValue: lazyValue'); - }); + expect(createModule(MyModule).injector.get('eager')).toBe('eagerValue: lazyValue'); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should inject providers that were declared after it', () => { - @NgModule({ - providers: [ - { - provide: 'eager', - useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`, - deps: [Injector] - }, - {provide: 'lazy', useFactory: () => 'lazyValue'}, - ] - }) - class MyModule { - // NgModule is eager, which makes all of its deps eager - constructor(@Inject('eager') eager: any) {} - } + it('should inject providers that were declared after it', () => { + @NgModule({ + providers: [ + { + provide: 'eager', + useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`, + deps: [Injector] + }, + {provide: 'lazy', useFactory: () => 'lazyValue'}, + ] + }) + class MyModule { + // NgModule is eager, which makes all of its deps eager + constructor(@Inject('eager') eager: any) {} + } - expect(createModule(MyModule).injector.get('eager')).toBe('eagerValue: lazyValue'); - }); + expect(createModule(MyModule).injector.get('eager')).toBe('eagerValue: lazyValue'); + }); }); describe('injecting eager providers into an eager provider via Injector.get', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should inject providers that were declared before it', () => { - @NgModule({ - providers: [ - {provide: 'eager1', useFactory: () => 'v1'}, - { - provide: 'eager2', - useFactory: (i: Injector) => `v2: ${i.get('eager1')}`, - deps: [Injector] - }, - ] - }) - class MyModule { - // NgModule is eager, which makes all of its deps eager - constructor(@Inject('eager1') eager1: any, @Inject('eager2') eager2: any) {} - } + it('should inject providers that were declared before it', () => { + @NgModule({ + providers: [ + {provide: 'eager1', useFactory: () => 'v1'}, + { + provide: 'eager2', + useFactory: (i: Injector) => `v2: ${i.get('eager1')}`, + deps: [Injector] + }, + ] + }) + class MyModule { + // NgModule is eager, which makes all of its deps eager + constructor(@Inject('eager1') eager1: any, @Inject('eager2') eager2: any) {} + } - expect(createModule(MyModule).injector.get('eager2')).toBe('v2: v1'); - }); + expect(createModule(MyModule).injector.get('eager2')).toBe('v2: v1'); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should inject providers that were declared after it', () => { - @NgModule({ - providers: [ - { - provide: 'eager1', - useFactory: (i: Injector) => `v1: ${i.get('eager2')}`, - deps: [Injector] - }, - {provide: 'eager2', useFactory: () => 'v2'}, - ] - }) - class MyModule { - // NgModule is eager, which makes all of its deps eager - constructor(@Inject('eager1') eager1: any, @Inject('eager2') eager2: any) {} - } + it('should inject providers that were declared after it', () => { + @NgModule({ + providers: [ + { + provide: 'eager1', + useFactory: (i: Injector) => `v1: ${i.get('eager2')}`, + deps: [Injector] + }, + {provide: 'eager2', useFactory: () => 'v2'}, + ] + }) + class MyModule { + // NgModule is eager, which makes all of its deps eager + constructor(@Inject('eager1') eager1: any, @Inject('eager2') eager2: any) {} + } - expect(createModule(MyModule).injector.get('eager1')).toBe('v1: v2'); - }); + expect(createModule(MyModule).injector.get('eager1')).toBe('v1: v2'); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('eager providers should get initialized only once', () => { - @Injectable() - class MyService1 { - public innerService: MyService2; - constructor(injector: Injector) { - // Create MyService2 before it it's initialized by TestModule. - this.innerService = injector.get(MyService2); - } - } + it('eager providers should get initialized only once', () => { + @Injectable() + class MyService1 { + public innerService: MyService2; + constructor(injector: Injector) { + // Create MyService2 before it it's initialized by TestModule. + this.innerService = injector.get(MyService2); + } + } - @Injectable() - class MyService2 { - constructor() {} - } + @Injectable() + class MyService2 { + constructor() {} + } - @NgModule({ - providers: [MyService1, MyService2], - }) - class TestModule { - constructor(public service1: MyService1, public service2: MyService2) {} - } + @NgModule({ + providers: [MyService1, MyService2], + }) + class TestModule { + constructor(public service1: MyService1, public service2: MyService2) {} + } - const moduleRef = createModule(TestModule, injector); - const module = moduleRef.instance; + const moduleRef = createModule(TestModule, injector); + const module = moduleRef.instance; - // MyService2 should not get initialized twice. - expect(module.service1.innerService).toBe(module.service2); - }); + // MyService2 should not get initialized twice. + expect(module.service1.innerService).toBe(module.service2); + }); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should throw when no provider defined', () => { - const injector = createInjector([]); - expect(() => injector.get('NonExisting')) - .toThrowError('NullInjectorError: No provider for NonExisting!'); - }); + it('should throw when no provider defined', () => { + const injector = createInjector([]); + expect(() => injector.get('NonExisting')) + .toThrowError('NullInjectorError: No provider for NonExisting!'); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('FW-682: Compiler error handling') && it('should throw when trying to instantiate a cyclic dependency', () => { expect(() => createInjector([Car, {provide: Engine, useClass: CyclicEngine}])) .toThrowError(/Cannot instantiate cyclic dependency! Car/g); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should support null values', () => { + it('should support null values', () => { const injector = createInjector([{provide: 'null', useValue: null}]); expect(injector.get('null')).toBe(null); }); describe('child', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should load instances from parent injector', () => { - const parent = createInjector([Engine]); - const child = createInjector([], parent); + it('should load instances from parent injector', () => { + const parent = createInjector([Engine]); + const child = createInjector([], parent); - const engineFromParent = parent.get(Engine); - const engineFromChild = child.get(Engine); + const engineFromParent = parent.get(Engine); + const engineFromChild = child.get(Engine); - expect(engineFromChild).toBe(engineFromParent); - }); + expect(engineFromChild).toBe(engineFromParent); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should not use the child providers when resolving the dependencies of a parent provider', - () => { - const parent = createInjector([Car, Engine]); - const child = createInjector([{provide: Engine, useClass: TurboEngine}], parent); + it('should not use the child providers when resolving the dependencies of a parent provider', + () => { + const parent = createInjector([Car, Engine]); + const child = createInjector([{provide: Engine, useClass: TurboEngine}], parent); - const carFromChild = child.get(Car); - expect(carFromChild.engine).toBeAnInstanceOf(Engine); - }); + const carFromChild = child.get(Car); + expect(carFromChild.engine).toBeAnInstanceOf(Engine); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should create new instance in a child injector', () => { - const parent = createInjector([Engine]); - const child = createInjector([{provide: Engine, useClass: TurboEngine}], parent); + it('should create new instance in a child injector', () => { + const parent = createInjector([Engine]); + const child = createInjector([{provide: Engine, useClass: TurboEngine}], parent); - const engineFromParent = parent.get(Engine); - const engineFromChild = child.get(Engine); + const engineFromParent = parent.get(Engine); + const engineFromChild = child.get(Engine); - expect(engineFromParent).not.toBe(engineFromChild); - expect(engineFromChild).toBeAnInstanceOf(TurboEngine); - }); + expect(engineFromParent).not.toBe(engineFromChild); + expect(engineFromChild).toBeAnInstanceOf(TurboEngine); + }); }); describe('depedency resolution', () => { describe('@Self()', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should return a dependency from self', () => { - const inj = createInjector([ - Engine, { - provide: Car, - useFactory: (e: Engine) => new Car(e), - deps: [[Engine, new Self()]] - } - ]); + it('should return a dependency from self', () => { + const inj = createInjector([ + Engine, + {provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]} + ]); - expect(inj.get(Car)).toBeAnInstanceOf(Car); - }); + expect(inj.get(Car)).toBeAnInstanceOf(Car); + }); }); describe('default', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && it('should not skip self', () => { + it('should not skip self', () => { const parent = createInjector([Engine]); const child = createInjector( [ @@ -1067,7 +1017,7 @@ function declareTests(config?: {useJit: boolean}) { }); describe('lifecycle', () => { - fixmeIvy('unknown') && it('should instantiate modules eagerly', () => { + it('should instantiate modules eagerly', () => { let created = false; @NgModule() @@ -1084,22 +1034,21 @@ function declareTests(config?: {useJit: boolean}) { expect(created).toBe(true); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should instantiate providers that are not used by a module lazily', () => { - let created = false; + it('should instantiate providers that are not used by a module lazily', () => { + let created = false; - createInjector([{ - provide: 'someToken', - useFactory: () => { - created = true; - return true; - } - }]); + createInjector([{ + provide: 'someToken', + useFactory: () => { + created = true; + return true; + } + }]); - expect(created).toBe(false); - }); + expect(created).toBe(false); + }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('ngOnDestroy not running') && it('should support ngOnDestroy on any provider', () => { let destroyed = false; @@ -1119,7 +1068,7 @@ function declareTests(config?: {useJit: boolean}) { expect(destroyed).toBe(true); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('ngOnDestroy not running') && it('should support ngOnDestroy for lazy providers', () => { let created = false; let destroyed = false; @@ -1151,233 +1100,220 @@ function declareTests(config?: {useJit: boolean}) { }); describe('imported and exported modules', () => { - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should add the providers of imported modules', () => { - @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) - class ImportedModule { - } + it('should add the providers of imported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) + class ImportedModule { + } - @NgModule({imports: [ImportedModule]}) - class SomeModule { - } + @NgModule({imports: [ImportedModule]}) + class SomeModule { + } - const injector = createModule(SomeModule).injector; + const injector = createModule(SomeModule).injector; - expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule); - expect(injector.get(ImportedModule)).toBeAnInstanceOf(ImportedModule); - expect(injector.get('token1')).toBe('imported'); - }); + expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule); + expect(injector.get(ImportedModule)).toBeAnInstanceOf(ImportedModule); + expect(injector.get('token1')).toBe('imported'); + }); - fixmeIvy('unknown') && - it('should add the providers of imported ModuleWithProviders', () => { - @NgModule() - class ImportedModule { - } - @NgModule({ - imports: [{ - ngModule: ImportedModule, - providers: [{provide: 'token1', useValue: 'imported'}] - }] - }) - class SomeModule { - } + it('should add the providers of imported ModuleWithProviders', () => { + @NgModule() + class ImportedModule { + } - const injector = createModule(SomeModule).injector; + @NgModule({ + imports: [ + {ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]} + ] + }) + class SomeModule { + } - expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule); - expect(injector.get(ImportedModule)).toBeAnInstanceOf(ImportedModule); - expect(injector.get('token1')).toBe('imported'); - }); + const injector = createModule(SomeModule).injector; - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should overwrite the providers of imported modules', () => { - @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) - class ImportedModule { - } + expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule); + expect(injector.get(ImportedModule)).toBeAnInstanceOf(ImportedModule); + expect(injector.get('token1')).toBe('imported'); + }); - @NgModule( - {providers: [{provide: 'token1', useValue: 'direct'}], imports: [ImportedModule]}) - class SomeModule { - } + it('should overwrite the providers of imported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) + class ImportedModule { + } - const injector = createModule(SomeModule).injector; - expect(injector.get('token1')).toBe('direct'); - }); + @NgModule( + {providers: [{provide: 'token1', useValue: 'direct'}], imports: [ImportedModule]}) + class SomeModule { + } - fixmeIvy('unknown') && - it('should overwrite the providers of imported ModuleWithProviders', () => { - @NgModule() - class ImportedModule { - } + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('direct'); + }); - @NgModule({ - providers: [{provide: 'token1', useValue: 'direct'}], - imports: [{ - ngModule: ImportedModule, - providers: [{provide: 'token1', useValue: 'imported'}] - }] - }) - class SomeModule { - } - const injector = createModule(SomeModule).injector; - expect(injector.get('token1')).toBe('direct'); - }); + it('should overwrite the providers of imported ModuleWithProviders', () => { + @NgModule() + class ImportedModule { + } - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should overwrite the providers of imported modules on the second import level', - () => { - @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) - class ImportedModuleLevel2 { - } + @NgModule({ + providers: [{provide: 'token1', useValue: 'direct'}], + imports: [ + {ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]} + ] + }) + class SomeModule { + } - @NgModule({ - providers: [{provide: 'token1', useValue: 'direct'}], - imports: [ImportedModuleLevel2] - }) - class ImportedModuleLevel1 { - } + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('direct'); + }); - @NgModule({imports: [ImportedModuleLevel1]}) - class SomeModule { - } + it('should overwrite the providers of imported modules on the second import level', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) + class ImportedModuleLevel2 { + } - const injector = createModule(SomeModule).injector; - expect(injector.get('token1')).toBe('direct'); - }); + @NgModule({ + providers: [{provide: 'token1', useValue: 'direct'}], + imports: [ImportedModuleLevel2] + }) + class ImportedModuleLevel1 { + } - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should add the providers of exported modules', () => { - @NgModule({providers: [{provide: 'token1', useValue: 'exported'}]}) - class ExportedValue { - } + @NgModule({imports: [ImportedModuleLevel1]}) + class SomeModule { + } - @NgModule({exports: [ExportedValue]}) - class SomeModule { - } + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('direct'); + }); - const injector = createModule(SomeModule).injector; + it('should add the providers of exported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'exported'}]}) + class ExportedValue { + } - expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule); - expect(injector.get(ExportedValue)).toBeAnInstanceOf(ExportedValue); - expect(injector.get('token1')).toBe('exported'); - }); + @NgModule({exports: [ExportedValue]}) + class SomeModule { + } - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should overwrite the providers of exported modules', () => { - @NgModule({providers: [{provide: 'token1', useValue: 'exported'}]}) - class ExportedModule { - } + const injector = createModule(SomeModule).injector; - @NgModule( - {providers: [{provide: 'token1', useValue: 'direct'}], exports: [ExportedModule]}) - class SomeModule { - } + expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule); + expect(injector.get(ExportedValue)).toBeAnInstanceOf(ExportedValue); + expect(injector.get('token1')).toBe('exported'); + }); - const injector = createModule(SomeModule).injector; - expect(injector.get('token1')).toBe('direct'); - }); + it('should overwrite the providers of exported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'exported'}]}) + class ExportedModule { + } - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should overwrite the providers of imported modules by following imported modules', - () => { - @NgModule({providers: [{provide: 'token1', useValue: 'imported1'}]}) - class ImportedModule1 { - } + @NgModule( + {providers: [{provide: 'token1', useValue: 'direct'}], exports: [ExportedModule]}) + class SomeModule { + } - @NgModule({providers: [{provide: 'token1', useValue: 'imported2'}]}) - class ImportedModule2 { - } + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('direct'); + }); - @NgModule({imports: [ImportedModule1, ImportedModule2]}) - class SomeModule { - } + it('should overwrite the providers of imported modules by following imported modules', + () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported1'}]}) + class ImportedModule1 { + } - const injector = createModule(SomeModule).injector; - expect(injector.get('token1')).toBe('imported2'); - }); + @NgModule({providers: [{provide: 'token1', useValue: 'imported2'}]}) + class ImportedModule2 { + } - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should overwrite the providers of exported modules by following exported modules', - () => { - @NgModule({providers: [{provide: 'token1', useValue: 'exported1'}]}) - class ExportedModule1 { - } + @NgModule({imports: [ImportedModule1, ImportedModule2]}) + class SomeModule { + } - @NgModule({providers: [{provide: 'token1', useValue: 'exported2'}]}) - class ExportedModule2 { - } + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('imported2'); + }); - @NgModule({exports: [ExportedModule1, ExportedModule2]}) - class SomeModule { - } + it('should overwrite the providers of exported modules by following exported modules', + () => { + @NgModule({providers: [{provide: 'token1', useValue: 'exported1'}]}) + class ExportedModule1 { + } - const injector = createModule(SomeModule).injector; - expect(injector.get('token1')).toBe('exported2'); - }); + @NgModule({providers: [{provide: 'token1', useValue: 'exported2'}]}) + class ExportedModule2 { + } - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should overwrite the providers of imported modules by exported modules', () => { - @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) - class ImportedModule { - } + @NgModule({exports: [ExportedModule1, ExportedModule2]}) + class SomeModule { + } - @NgModule({providers: [{provide: 'token1', useValue: 'exported'}]}) - class ExportedModule { - } + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('exported2'); + }); - @NgModule({imports: [ImportedModule], exports: [ExportedModule]}) - class SomeModule { - } + it('should overwrite the providers of imported modules by exported modules', () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported'}]}) + class ImportedModule { + } - const injector = createModule(SomeModule).injector; - expect(injector.get('token1')).toBe('exported'); - }); + @NgModule({providers: [{provide: 'token1', useValue: 'exported'}]}) + class ExportedModule { + } - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should not overwrite the providers if a module was already used on the same level', - () => { - @NgModule({providers: [{provide: 'token1', useValue: 'imported1'}]}) - class ImportedModule1 { - } + @NgModule({imports: [ImportedModule], exports: [ExportedModule]}) + class SomeModule { + } - @NgModule({providers: [{provide: 'token1', useValue: 'imported2'}]}) - class ImportedModule2 { - } + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('exported'); + }); - @NgModule({imports: [ImportedModule1, ImportedModule2, ImportedModule1]}) - class SomeModule { - } + it('should not overwrite the providers if a module was already used on the same level', + () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported1'}]}) + class ImportedModule1 { + } - const injector = createModule(SomeModule).injector; - expect(injector.get('token1')).toBe('imported2'); - }); + @NgModule({providers: [{provide: 'token1', useValue: 'imported2'}]}) + class ImportedModule2 { + } - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should not overwrite the providers if a module was already used on a child level', - () => { - @NgModule({providers: [{provide: 'token1', useValue: 'imported1'}]}) - class ImportedModule1 { - } + @NgModule({imports: [ImportedModule1, ImportedModule2, ImportedModule1]}) + class SomeModule { + } - @NgModule({imports: [ImportedModule1]}) - class ImportedModule3 { - } + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('imported2'); + }); - @NgModule({providers: [{provide: 'token1', useValue: 'imported2'}]}) - class ImportedModule2 { - } + it('should not overwrite the providers if a module was already used on a child level', + () => { + @NgModule({providers: [{provide: 'token1', useValue: 'imported1'}]}) + class ImportedModule1 { + } - @NgModule({imports: [ImportedModule3, ImportedModule2, ImportedModule1]}) - class SomeModule { - } + @NgModule({imports: [ImportedModule1]}) + class ImportedModule3 { + } - const injector = createModule(SomeModule).injector; - expect(injector.get('token1')).toBe('imported2'); - }); + @NgModule({providers: [{provide: 'token1', useValue: 'imported2'}]}) + class ImportedModule2 { + } - fixmeIvy('unknown') && + @NgModule({imports: [ImportedModule3, ImportedModule2, ImportedModule1]}) + class SomeModule { + } + + const injector = createModule(SomeModule).injector; + expect(injector.get('token1')).toBe('imported2'); + }); + + fixmeIvy('FW-682: Compiler error handling') && it('should throw when given invalid providers in an imported ModuleWithProviders', () => { @NgModule() class ImportedModule1 { @@ -1394,7 +1330,7 @@ function declareTests(config?: {useJit: boolean}) { }); describe('tree shakable providers', () => { - fixmeIvy('unknown') && + fixmeIvy('providersByKey is not defined') && it('definition should not persist across NgModuleRef instances', () => { @NgModule() class SomeModule { diff --git a/packages/core/test/render3/control_flow_spec.ts b/packages/core/test/render3/control_flow_spec.ts index fc5b857f83..e333402a20 100644 --- a/packages/core/test/render3/control_flow_spec.ts +++ b/packages/core/test/render3/control_flow_spec.ts @@ -10,7 +10,7 @@ import {defineComponent} from '../../src/render3/definition'; import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, text, textBinding} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; -import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util'; +import {ComponentFixture, TemplateFixture, createComponent} from './render_util'; describe('JS control flow', () => { it('should work with if block', () => { diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 63dd2285a7..5a3f366817 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -2021,8 +2021,8 @@ describe('di', () => { describe('getOrCreateNodeInjector', () => { it('should handle initial undefined state', () => { const contentView = createLViewData( - null, null !, createTView(-1, null, 1, 0, null, null, null), null, - LViewFlags.CheckAlways); + null, createTView(-1, null, 1, 0, null, null, null), null, LViewFlags.CheckAlways, + {} as any, {} as any); const oldView = enterView(contentView, null); try { const parentTNode = createNodeAtIndex(0, TNodeType.Element, null, null, null); diff --git a/packages/core/test/render3/styling/class_and_style_bindings_spec.ts b/packages/core/test/render3/styling/class_and_style_bindings_spec.ts index d945875e51..6c53cf783d 100644 --- a/packages/core/test/render3/styling/class_and_style_bindings_spec.ts +++ b/packages/core/test/render3/styling/class_and_style_bindings_spec.ts @@ -33,8 +33,8 @@ describe('style and class based bindings', () => { const rootContext = createRootContext(requestAnimationFrame.bind(window), playerHandler || null); const lViewData = createLViewData( - null, domRendererFactory3.createRenderer(element, null), - createTView(-1, null, 1, 0, null, null, null), rootContext, LViewFlags.IsRoot); + null, createTView(-1, null, 1, 0, null, null, null), rootContext, LViewFlags.IsRoot, + domRendererFactory3, domRendererFactory3.createRenderer(element, null)); return lViewData; } diff --git a/packages/core/testing/src/r3_test_bed.ts b/packages/core/testing/src/r3_test_bed.ts index a69b26409b..59dcff0532 100644 --- a/packages/core/testing/src/r3_test_bed.ts +++ b/packages/core/testing/src/r3_test_bed.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Directive, Injector, NgModule, NgZone, Pipe, PlatformRef, Provider, RendererFactory2, SchemaMetadata, Type, ɵInjectableDef as InjectableDef, ɵNgModuleDef as NgModuleDef, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵRender3ComponentFactory as ComponentFactory, ɵRender3DebugRendererFactory2 as Render3DebugRendererFactory2, ɵRender3NgModuleRef as NgModuleRef, ɵWRAP_RENDERER_FACTORY2 as WRAP_RENDERER_FACTORY2, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵgetInjectableDef as getInjectableDef, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵstringify as stringify} from '@angular/core'; +import {Component, Directive, Injector, NgModule, NgZone, Pipe, PlatformRef, Provider, RendererFactory2, SchemaMetadata, Type, ɵInjectableDef as InjectableDef, ɵNgModuleDef as NgModuleDef, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵRender3ComponentFactory as ComponentFactory, ɵRender3NgModuleRef as NgModuleRef, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵgetInjectableDef as getInjectableDef, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵstringify as stringify} from '@angular/core'; import {ComponentFixture} from './component_fixture'; import {MetadataOverride} from './metadata_override'; @@ -423,13 +423,8 @@ export class TestBedRender3 implements Injector, TestBed { private _createTestModule(): Type { const rootProviderOverrides = this._rootProviderOverrides; - const rendererFactoryWrapper = { - provide: WRAP_RENDERER_FACTORY2, - useFactory: () => (rf: RendererFactory2) => new Render3DebugRendererFactory2(rf), - }; - @NgModule({ - providers: [...rootProviderOverrides, rendererFactoryWrapper], + providers: [...rootProviderOverrides], jit: true, }) class RootScopeModule { diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 5181dba960..9c62f6e47e 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -9,7 +9,7 @@ import {CommonModule, Location} from '@angular/common'; import {SpyLocation} from '@angular/common/testing'; import {ChangeDetectionStrategy, Component, Injectable, NgModule, NgModuleFactoryLoader, NgModuleRef, NgZone, OnDestroy, ɵConsole as Console, ɵNoopNgZone as NoopNgZone} from '@angular/core'; -import {ComponentFixture, TestBed, fakeAsync, flush, inject, tick} from '@angular/core/testing'; +import {ComponentFixture, TestBed, fakeAsync, inject, tick} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {fixmeIvy} from '@angular/private/testing'; @@ -2482,45 +2482,46 @@ describe('Integration', () => { expect(canceledStatus).toEqual(false); }))); - it('works with componentless routes', - fakeAsync(inject([Router, Location], (router: Router, location: Location) => { - const fixture = createRoot(router, RootCmp); + fixmeIvy('unknown') && + it('works with componentless routes', + fakeAsync(inject([Router, Location], (router: Router, location: Location) => { + const fixture = createRoot(router, RootCmp); - router.resetConfig([ - { - path: 'grandparent', - canDeactivate: ['RecordingDeactivate'], - children: [{ - path: 'parent', - canDeactivate: ['RecordingDeactivate'], - children: [{ - path: 'child', + router.resetConfig([ + { + path: 'grandparent', canDeactivate: ['RecordingDeactivate'], children: [{ - path: 'simple', - component: SimpleCmp, - canDeactivate: ['RecordingDeactivate'] + path: 'parent', + canDeactivate: ['RecordingDeactivate'], + children: [{ + path: 'child', + canDeactivate: ['RecordingDeactivate'], + children: [{ + path: 'simple', + component: SimpleCmp, + canDeactivate: ['RecordingDeactivate'] + }] + }] }] - }] - }] - }, - {path: 'simple', component: SimpleCmp} - ]); + }, + {path: 'simple', component: SimpleCmp} + ]); - router.navigateByUrl('/grandparent/parent/child/simple'); - advance(fixture); - expect(location.path()).toEqual('/grandparent/parent/child/simple'); + router.navigateByUrl('/grandparent/parent/child/simple'); + advance(fixture); + expect(location.path()).toEqual('/grandparent/parent/child/simple'); - router.navigateByUrl('/simple'); - advance(fixture); + router.navigateByUrl('/simple'); + advance(fixture); - const child = fixture.debugElement.children[1].componentInstance; + const child = fixture.debugElement.children[1].componentInstance; - expect(log.map((a: any) => a.path)).toEqual([ - 'simple', 'child', 'parent', 'grandparent' - ]); - expect(log.map((a: any) => a.component)).toEqual([child, null, null, null]); - }))); + expect(log.map((a: any) => a.path)).toEqual([ + 'simple', 'child', 'parent', 'grandparent' + ]); + expect(log.map((a: any) => a.component)).toEqual([child, null, null, null]); + }))); it('works with aux routes', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { @@ -2823,7 +2824,7 @@ describe('Integration', () => { }))); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('unknown') && it('should find the guard provided in lazy loaded module', fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], @@ -2892,76 +2893,75 @@ describe('Integration', () => { }); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should not load children when CanLoad returns false', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + it('should not load children when CanLoad returns false', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) - class LazyLoadedComponent { - } + @Component({selector: 'lazy', template: 'lazy-loaded'}) + class LazyLoadedComponent { + } - @NgModule({ - declarations: [LazyLoadedComponent], - imports: [RouterModule.forChild( - [{path: 'loaded', component: LazyLoadedComponent}])] - }) - class LoadedModule { - } + @NgModule({ + declarations: [LazyLoadedComponent], + imports: + [RouterModule.forChild([{path: 'loaded', component: LazyLoadedComponent}])] + }) + class LoadedModule { + } - loader.stubbedModules = {lazyFalse: LoadedModule, lazyTrue: LoadedModule}; - const fixture = createRoot(router, RootCmp); + loader.stubbedModules = {lazyFalse: LoadedModule, lazyTrue: LoadedModule}; + const fixture = createRoot(router, RootCmp); - router.resetConfig([ - {path: 'lazyFalse', canLoad: ['alwaysFalse'], loadChildren: 'lazyFalse'}, - {path: 'lazyTrue', canLoad: ['alwaysTrue'], loadChildren: 'lazyTrue'} - ]); + router.resetConfig([ + {path: 'lazyFalse', canLoad: ['alwaysFalse'], loadChildren: 'lazyFalse'}, + {path: 'lazyTrue', canLoad: ['alwaysTrue'], loadChildren: 'lazyTrue'} + ]); - const recordedEvents: any[] = []; - router.events.forEach(e => recordedEvents.push(e)); + const recordedEvents: any[] = []; + router.events.forEach(e => recordedEvents.push(e)); - // failed navigation - router.navigateByUrl('/lazyFalse/loaded'); - advance(fixture); + // failed navigation + router.navigateByUrl('/lazyFalse/loaded'); + advance(fixture); - expect(location.path()).toEqual('/'); + expect(location.path()).toEqual('/'); - expectEvents(recordedEvents, [ - [NavigationStart, '/lazyFalse/loaded'], - // [GuardsCheckStart, '/lazyFalse/loaded'], - [NavigationCancel, '/lazyFalse/loaded'], - ]); + expectEvents(recordedEvents, [ + [NavigationStart, '/lazyFalse/loaded'], + // [GuardsCheckStart, '/lazyFalse/loaded'], + [NavigationCancel, '/lazyFalse/loaded'], + ]); - recordedEvents.splice(0); + recordedEvents.splice(0); - // successful navigation - router.navigateByUrl('/lazyTrue/loaded'); - advance(fixture); + // successful navigation + router.navigateByUrl('/lazyTrue/loaded'); + advance(fixture); - expect(location.path()).toEqual('/lazyTrue/loaded'); + expect(location.path()).toEqual('/lazyTrue/loaded'); - expectEvents(recordedEvents, [ - [NavigationStart, '/lazyTrue/loaded'], - [RouteConfigLoadStart], - [RouteConfigLoadEnd], - [RoutesRecognized, '/lazyTrue/loaded'], - [GuardsCheckStart, '/lazyTrue/loaded'], - [ChildActivationStart], - [ActivationStart], - [ChildActivationStart], - [ActivationStart], - [GuardsCheckEnd, '/lazyTrue/loaded'], - [ResolveStart, '/lazyTrue/loaded'], - [ResolveEnd, '/lazyTrue/loaded'], - [ActivationEnd], - [ChildActivationEnd], - [ActivationEnd], - [ChildActivationEnd], - [NavigationEnd, '/lazyTrue/loaded'], - ]); - }))); + expectEvents(recordedEvents, [ + [NavigationStart, '/lazyTrue/loaded'], + [RouteConfigLoadStart], + [RouteConfigLoadEnd], + [RoutesRecognized, '/lazyTrue/loaded'], + [GuardsCheckStart, '/lazyTrue/loaded'], + [ChildActivationStart], + [ActivationStart], + [ChildActivationStart], + [ActivationStart], + [GuardsCheckEnd, '/lazyTrue/loaded'], + [ResolveStart, '/lazyTrue/loaded'], + [ResolveEnd, '/lazyTrue/loaded'], + [ActivationEnd], + [ChildActivationEnd], + [ActivationEnd], + [ChildActivationEnd], + [NavigationEnd, '/lazyTrue/loaded'], + ]); + }))); it('should support navigating from within the guard', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { @@ -2997,82 +2997,79 @@ describe('Integration', () => { // Regression where navigateByUrl with false CanLoad no longer resolved `false` value on // navigateByUrl promise: https://github.com/angular/angular/issues/26284 - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should resolve navigateByUrl promise after CanLoad executes', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + it('should resolve navigateByUrl promise after CanLoad executes', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) - class LazyLoadedComponent { - } + @Component({selector: 'lazy', template: 'lazy-loaded'}) + class LazyLoadedComponent { + } - @NgModule({ - declarations: [LazyLoadedComponent], - imports: [RouterModule.forChild( - [{path: 'loaded', component: LazyLoadedComponent}])] - }) - class LazyLoadedModule { - } + @NgModule({ + declarations: [LazyLoadedComponent], + imports: + [RouterModule.forChild([{path: 'loaded', component: LazyLoadedComponent}])] + }) + class LazyLoadedModule { + } - loader.stubbedModules = {lazy: LazyLoadedModule}; - const fixture = createRoot(router, RootCmp); + loader.stubbedModules = {lazy: LazyLoadedModule}; + const fixture = createRoot(router, RootCmp); - router.resetConfig([ - {path: 'lazy-false', canLoad: ['alwaysFalse'], loadChildren: 'lazy'}, - {path: 'lazy-true', canLoad: ['alwaysTrue'], loadChildren: 'lazy'}, - ]); + router.resetConfig([ + {path: 'lazy-false', canLoad: ['alwaysFalse'], loadChildren: 'lazy'}, + {path: 'lazy-true', canLoad: ['alwaysTrue'], loadChildren: 'lazy'}, + ]); - let navFalseResult: any; - let navTrueResult: any; - router.navigateByUrl('/lazy-false').then(v => { navFalseResult = v; }); - advance(fixture); - router.navigateByUrl('/lazy-true').then(v => { navTrueResult = v; }); - advance(fixture); + let navFalseResult: any; + let navTrueResult: any; + router.navigateByUrl('/lazy-false').then(v => { navFalseResult = v; }); + advance(fixture); + router.navigateByUrl('/lazy-true').then(v => { navTrueResult = v; }); + advance(fixture); - expect(navFalseResult).toBe(false); - expect(navTrueResult).toBe(true); + expect(navFalseResult).toBe(false); + expect(navTrueResult).toBe(true); - }))); + }))); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should execute CanLoad only once', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + it('should execute CanLoad only once', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) - class LazyLoadedComponent { - } + @Component({selector: 'lazy', template: 'lazy-loaded'}) + class LazyLoadedComponent { + } - @NgModule({ - declarations: [LazyLoadedComponent], - imports: [RouterModule.forChild( - [{path: 'loaded', component: LazyLoadedComponent}])] - }) - class LazyLoadedModule { - } + @NgModule({ + declarations: [LazyLoadedComponent], + imports: + [RouterModule.forChild([{path: 'loaded', component: LazyLoadedComponent}])] + }) + class LazyLoadedModule { + } - loader.stubbedModules = {lazy: LazyLoadedModule}; - const fixture = createRoot(router, RootCmp); + loader.stubbedModules = {lazy: LazyLoadedModule}; + const fixture = createRoot(router, RootCmp); - router.resetConfig( - [{path: 'lazy', canLoad: ['alwaysTrue'], loadChildren: 'lazy'}]); + router.resetConfig([{path: 'lazy', canLoad: ['alwaysTrue'], loadChildren: 'lazy'}]); - router.navigateByUrl('/lazy/loaded'); - advance(fixture); - expect(location.path()).toEqual('/lazy/loaded'); - expect(canLoadRunCount).toEqual(1); + router.navigateByUrl('/lazy/loaded'); + advance(fixture); + expect(location.path()).toEqual('/lazy/loaded'); + expect(canLoadRunCount).toEqual(1); - router.navigateByUrl('/'); - advance(fixture); - expect(location.path()).toEqual('/'); + router.navigateByUrl('/'); + advance(fixture); + expect(location.path()).toEqual('/'); - router.navigateByUrl('/lazy/loaded'); - advance(fixture); - expect(location.path()).toEqual('/lazy/loaded'); - expect(canLoadRunCount).toEqual(1); - }))); + router.navigateByUrl('/lazy/loaded'); + advance(fixture); + expect(location.path()).toEqual('/lazy/loaded'); + expect(canLoadRunCount).toEqual(1); + }))); }); describe('order', () => { @@ -3389,48 +3386,49 @@ describe('Integration', () => { }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && describe('lazy loading', () => { - it('works', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({ - selector: 'lazy', - template: 'lazy-loaded-parent []' - }) - class ParentLazyLoadedComponent { - } + describe('lazy loading', () => { + fixmeIvy('unknown') && + it('works', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + @Component({ + selector: 'lazy', + template: 'lazy-loaded-parent []' + }) + class ParentLazyLoadedComponent { + } - @Component({selector: 'lazy', template: 'lazy-loaded-child'}) - class ChildLazyLoadedComponent { - } + @Component({selector: 'lazy', template: 'lazy-loaded-child'}) + class ChildLazyLoadedComponent { + } - @NgModule({ - declarations: [ParentLazyLoadedComponent, ChildLazyLoadedComponent], - imports: [RouterModule.forChild([{ - path: 'loaded', - component: ParentLazyLoadedComponent, - children: [{path: 'child', component: ChildLazyLoadedComponent}] - }])] - }) - class LoadedModule { - } + @NgModule({ + declarations: [ParentLazyLoadedComponent, ChildLazyLoadedComponent], + imports: [RouterModule.forChild([{ + path: 'loaded', + component: ParentLazyLoadedComponent, + children: [{path: 'child', component: ChildLazyLoadedComponent}] + }])] + }) + class LoadedModule { + } - loader.stubbedModules = {expected: LoadedModule}; + loader.stubbedModules = {expected: LoadedModule}; - const fixture = createRoot(router, RootCmp); + const fixture = createRoot(router, RootCmp); - router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); + router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); - router.navigateByUrl('/lazy/loaded/child'); - advance(fixture); + router.navigateByUrl('/lazy/loaded/child'); + advance(fixture); - expect(location.path()).toEqual('/lazy/loaded/child'); - expect(fixture.nativeElement).toHaveText('lazy-loaded-parent [lazy-loaded-child]'); - }))); + expect(location.path()).toEqual('/lazy/loaded/child'); + expect(fixture.nativeElement).toHaveText('lazy-loaded-parent [lazy-loaded-child]'); + }))); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('timeout') && it('should have 2 injector trees: module and element', fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], @@ -3520,7 +3518,7 @@ describe('Integration', () => { }))); // https://github.com/angular/angular/issues/12889 - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('unknown') && it('should create a single instance of lazy-loaded modules', fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], @@ -3559,7 +3557,7 @@ describe('Integration', () => { }))); // https://github.com/angular/angular/issues/13870 - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('timeout') && it('should create a single instance of guards for lazy-loaded modules', fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], @@ -3611,136 +3609,129 @@ describe('Integration', () => { }))); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should emit RouteConfigLoadStart and RouteConfigLoadEnd event when route is lazy loaded', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({ - selector: 'lazy', - template: 'lazy-loaded-parent []', - }) - class ParentLazyLoadedComponent { - } + it('should emit RouteConfigLoadStart and RouteConfigLoadEnd event when route is lazy loaded', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + @Component({ + selector: 'lazy', + template: 'lazy-loaded-parent []', + }) + class ParentLazyLoadedComponent { + } - @Component({selector: 'lazy', template: 'lazy-loaded-child'}) - class ChildLazyLoadedComponent { - } + @Component({selector: 'lazy', template: 'lazy-loaded-child'}) + class ChildLazyLoadedComponent { + } - @NgModule({ - declarations: [ParentLazyLoadedComponent, ChildLazyLoadedComponent], - imports: [RouterModule.forChild([{ - path: 'loaded', - component: ParentLazyLoadedComponent, - children: [{path: 'child', component: ChildLazyLoadedComponent}], - }])] - }) - class LoadedModule { - } + @NgModule({ + declarations: [ParentLazyLoadedComponent, ChildLazyLoadedComponent], + imports: [RouterModule.forChild([{ + path: 'loaded', + component: ParentLazyLoadedComponent, + children: [{path: 'child', component: ChildLazyLoadedComponent}], + }])] + }) + class LoadedModule { + } - const events: Array = []; + const events: Array = []; - router.events.subscribe(e => { - if (e instanceof RouteConfigLoadStart || e instanceof RouteConfigLoadEnd) { - events.push(e); - } - }); + router.events.subscribe(e => { + if (e instanceof RouteConfigLoadStart || e instanceof RouteConfigLoadEnd) { + events.push(e); + } + }); - loader.stubbedModules = {expected: LoadedModule}; - const fixture = createRoot(router, RootCmp); - router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); + loader.stubbedModules = {expected: LoadedModule}; + const fixture = createRoot(router, RootCmp); + router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); - router.navigateByUrl('/lazy/loaded/child'); - advance(fixture); + router.navigateByUrl('/lazy/loaded/child'); + advance(fixture); - expect(events.length).toEqual(2); - expect(events[0].toString()).toEqual('RouteConfigLoadStart(path: lazy)'); - expect(events[1].toString()).toEqual('RouteConfigLoadEnd(path: lazy)'); - }))); + expect(events.length).toEqual(2); + expect(events[0].toString()).toEqual('RouteConfigLoadStart(path: lazy)'); + expect(events[1].toString()).toEqual('RouteConfigLoadEnd(path: lazy)'); + }))); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('throws an error when forRoot() is used in a lazy context', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'should not show'}) - class LazyLoadedComponent { - } + it('throws an error when forRoot() is used in a lazy context', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + @Component({selector: 'lazy', template: 'should not show'}) + class LazyLoadedComponent { + } - @NgModule({ - declarations: [LazyLoadedComponent], - imports: - [RouterModule.forRoot([{path: 'loaded', component: LazyLoadedComponent}])] - }) - class LoadedModule { - } + @NgModule({ + declarations: [LazyLoadedComponent], + imports: [RouterModule.forRoot([{path: 'loaded', component: LazyLoadedComponent}])] + }) + class LoadedModule { + } - loader.stubbedModules = {expected: LoadedModule}; + loader.stubbedModules = {expected: LoadedModule}; - const fixture = createRoot(router, RootCmp); + const fixture = createRoot(router, RootCmp); - router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); + router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); - let recordedError: any = null; - router.navigateByUrl('/lazy/loaded') !.catch(err => recordedError = err); - advance(fixture); - expect(recordedError.message) - .toEqual( - `RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`); - }))); + let recordedError: any = null; + router.navigateByUrl('/lazy/loaded') !.catch(err => recordedError = err); + advance(fixture); + expect(recordedError.message) + .toEqual( + `RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`); + }))); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should combine routes from multiple modules into a single configuration', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded-2'}) - class LazyComponent2 { - } + it('should combine routes from multiple modules into a single configuration', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + @Component({selector: 'lazy', template: 'lazy-loaded-2'}) + class LazyComponent2 { + } - @NgModule({ - declarations: [LazyComponent2], - imports: [RouterModule.forChild([{path: 'loaded', component: LazyComponent2}])] - }) - class SiblingOfLoadedModule { - } + @NgModule({ + declarations: [LazyComponent2], + imports: [RouterModule.forChild([{path: 'loaded', component: LazyComponent2}])] + }) + class SiblingOfLoadedModule { + } - @Component({selector: 'lazy', template: 'lazy-loaded-1'}) - class LazyComponent1 { - } + @Component({selector: 'lazy', template: 'lazy-loaded-1'}) + class LazyComponent1 { + } - @NgModule({ - declarations: [LazyComponent1], - imports: [ - RouterModule.forChild([{path: 'loaded', component: LazyComponent1}]), - SiblingOfLoadedModule - ] - }) - class LoadedModule { - } + @NgModule({ + declarations: [LazyComponent1], + imports: [ + RouterModule.forChild([{path: 'loaded', component: LazyComponent1}]), + SiblingOfLoadedModule + ] + }) + class LoadedModule { + } - loader.stubbedModules = { - expected1: LoadedModule, - expected2: SiblingOfLoadedModule - }; + loader.stubbedModules = {expected1: LoadedModule, expected2: SiblingOfLoadedModule}; - const fixture = createRoot(router, RootCmp); + const fixture = createRoot(router, RootCmp); - router.resetConfig([ - {path: 'lazy1', loadChildren: 'expected1'}, - {path: 'lazy2', loadChildren: 'expected2'} - ]); + router.resetConfig([ + {path: 'lazy1', loadChildren: 'expected1'}, + {path: 'lazy2', loadChildren: 'expected2'} + ]); - router.navigateByUrl('/lazy1/loaded'); - advance(fixture); - expect(location.path()).toEqual('/lazy1/loaded'); + router.navigateByUrl('/lazy1/loaded'); + advance(fixture); + expect(location.path()).toEqual('/lazy1/loaded'); - router.navigateByUrl('/lazy2/loaded'); - advance(fixture); - expect(location.path()).toEqual('/lazy2/loaded'); - }))); + router.navigateByUrl('/lazy2/loaded'); + advance(fixture); + expect(location.path()).toEqual('/lazy2/loaded'); + }))); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('unknown') && it('should allow lazy loaded module in named outlet', fakeAsync(inject( [Router, NgModuleFactoryLoader], @@ -3783,32 +3774,38 @@ describe('Integration', () => { .toHaveText('team 22 [ user john, right: lazy-loaded ]'); }))); - it('should allow componentless named outlet to render children', - fakeAsync(inject( - [Router, NgModuleFactoryLoader], (router: Router, loader: SpyNgModuleFactoryLoader) => { + fixmeIvy('unknown') && + it('should allow componentless named outlet to render children', + fakeAsync(inject( + [Router, NgModuleFactoryLoader], + (router: Router, loader: SpyNgModuleFactoryLoader) => { - const fixture = createRoot(router, RootCmp); + const fixture = createRoot(router, RootCmp); - router.resetConfig([{ - path: 'team/:id', - component: TeamCmp, - children: [ - {path: 'user/:name', component: UserCmp}, - {path: 'simple', outlet: 'right', children: [{path: '', component: SimpleCmp}]}, - ] - }]); + router.resetConfig([{ + path: 'team/:id', + component: TeamCmp, + children: [ + {path: 'user/:name', component: UserCmp}, + { + path: 'simple', + outlet: 'right', + children: [{path: '', component: SimpleCmp}] + }, + ] + }]); - router.navigateByUrl('/team/22/user/john'); - advance(fixture); + router.navigateByUrl('/team/22/user/john'); + advance(fixture); - expect(fixture.nativeElement).toHaveText('team 22 [ user john, right: ]'); + expect(fixture.nativeElement).toHaveText('team 22 [ user john, right: ]'); - router.navigateByUrl('/team/22/(user/john//right:simple)'); - advance(fixture); + router.navigateByUrl('/team/22/(user/john//right:simple)'); + advance(fixture); - expect(fixture.nativeElement).toHaveText('team 22 [ user john, right: simple ]'); - }))); + expect(fixture.nativeElement).toHaveText('team 22 [ user john, right: simple ]'); + }))); describe('should use the injector of the lazily-loaded configuration', () => { class LazyLoadedServiceDefinedInModule {} @@ -3869,7 +3866,7 @@ describe('Integration', () => { }); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + fixmeIvy('timeout') && it('should use the injector of the lazily-loaded configuration', fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], @@ -3892,32 +3889,30 @@ describe('Integration', () => { }))); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('works when given a callback', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], (router: Router, location: Location) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) - class LazyLoadedComponent { - } + it('works when given a callback', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], (router: Router, location: Location) => { + @Component({selector: 'lazy', template: 'lazy-loaded'}) + class LazyLoadedComponent { + } - @NgModule({ - declarations: [LazyLoadedComponent], - imports: - [RouterModule.forChild([{path: 'loaded', component: LazyLoadedComponent}])], - }) - class LoadedModule { - } + @NgModule({ + declarations: [LazyLoadedComponent], + imports: [RouterModule.forChild([{path: 'loaded', component: LazyLoadedComponent}])], + }) + class LoadedModule { + } - const fixture = createRoot(router, RootCmp); + const fixture = createRoot(router, RootCmp); - router.resetConfig([{path: 'lazy', loadChildren: () => LoadedModule}]); + router.resetConfig([{path: 'lazy', loadChildren: () => LoadedModule}]); - router.navigateByUrl('/lazy/loaded'); - advance(fixture); + router.navigateByUrl('/lazy/loaded'); + advance(fixture); - expect(location.path()).toEqual('/lazy/loaded'); - expect(fixture.nativeElement).toHaveText('lazy-loaded'); - }))); + expect(location.path()).toEqual('/lazy/loaded'); + expect(fixture.nativeElement).toHaveText('lazy-loaded'); + }))); it('error emit an error when cannot load a config', fakeAsync(inject( @@ -3943,62 +3938,59 @@ describe('Integration', () => { ]); }))); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should work with complex redirect rules', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) - class LazyLoadedComponent { - } + it('should work with complex redirect rules', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + @Component({selector: 'lazy', template: 'lazy-loaded'}) + class LazyLoadedComponent { + } - @NgModule({ - declarations: [LazyLoadedComponent], - imports: - [RouterModule.forChild([{path: 'loaded', component: LazyLoadedComponent}])], - }) - class LoadedModule { - } + @NgModule({ + declarations: [LazyLoadedComponent], + imports: [RouterModule.forChild([{path: 'loaded', component: LazyLoadedComponent}])], + }) + class LoadedModule { + } - loader.stubbedModules = {lazy: LoadedModule}; - const fixture = createRoot(router, RootCmp); + loader.stubbedModules = {lazy: LoadedModule}; + const fixture = createRoot(router, RootCmp); - router.resetConfig( - [{path: 'lazy', loadChildren: 'lazy'}, {path: '**', redirectTo: 'lazy'}]); + router.resetConfig( + [{path: 'lazy', loadChildren: 'lazy'}, {path: '**', redirectTo: 'lazy'}]); - router.navigateByUrl('/lazy/loaded'); - advance(fixture); + router.navigateByUrl('/lazy/loaded'); + advance(fixture); - expect(location.path()).toEqual('/lazy/loaded'); - }))); + expect(location.path()).toEqual('/lazy/loaded'); + }))); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should work with wildcard route', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) - class LazyLoadedComponent { - } + it('should work with wildcard route', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + @Component({selector: 'lazy', template: 'lazy-loaded'}) + class LazyLoadedComponent { + } - @NgModule({ - declarations: [LazyLoadedComponent], - imports: [RouterModule.forChild([{path: '', component: LazyLoadedComponent}])], - }) - class LazyLoadedModule { - } + @NgModule({ + declarations: [LazyLoadedComponent], + imports: [RouterModule.forChild([{path: '', component: LazyLoadedComponent}])], + }) + class LazyLoadedModule { + } - loader.stubbedModules = {lazy: LazyLoadedModule}; - const fixture = createRoot(router, RootCmp); + loader.stubbedModules = {lazy: LazyLoadedModule}; + const fixture = createRoot(router, RootCmp); - router.resetConfig([{path: '**', loadChildren: 'lazy'}]); + router.resetConfig([{path: '**', loadChildren: 'lazy'}]); - router.navigateByUrl('/lazy'); - advance(fixture); + router.navigateByUrl('/lazy'); + advance(fixture); - expect(location.path()).toEqual('/lazy'); - expect(fixture.nativeElement).toHaveText('lazy-loaded'); - }))); + expect(location.path()).toEqual('/lazy'); + expect(fixture.nativeElement).toHaveText('lazy-loaded'); + }))); describe('preloading', () => { beforeEach(() => { @@ -4008,52 +4000,50 @@ describe('Integration', () => { preloader.setUpPreloading(); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should work', - fakeAsync(inject( - [Router, Location, NgModuleFactoryLoader], - (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'should not show'}) - class LazyLoadedComponent { - } + it('should work', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + @Component({selector: 'lazy', template: 'should not show'}) + class LazyLoadedComponent { + } - @NgModule({ - declarations: [LazyLoadedComponent], - imports: [RouterModule.forChild( - [{path: 'LoadedModule2', component: LazyLoadedComponent}])] - }) - class LoadedModule2 { - } + @NgModule({ + declarations: [LazyLoadedComponent], + imports: [RouterModule.forChild( + [{path: 'LoadedModule2', component: LazyLoadedComponent}])] + }) + class LoadedModule2 { + } - @NgModule({ - imports: [RouterModule.forChild( - [{path: 'LoadedModule1', loadChildren: 'expected2'}])] - }) - class LoadedModule1 { - } + @NgModule({ + imports: + [RouterModule.forChild([{path: 'LoadedModule1', loadChildren: 'expected2'}])] + }) + class LoadedModule1 { + } - loader.stubbedModules = {expected: LoadedModule1, expected2: LoadedModule2}; + loader.stubbedModules = {expected: LoadedModule1, expected2: LoadedModule2}; - const fixture = createRoot(router, RootCmp); + const fixture = createRoot(router, RootCmp); - router.resetConfig([ - {path: 'blank', component: BlankCmp}, - {path: 'lazy', loadChildren: 'expected'} - ]); + router.resetConfig([ + {path: 'blank', component: BlankCmp}, {path: 'lazy', loadChildren: 'expected'} + ]); - router.navigateByUrl('/blank'); - advance(fixture); + router.navigateByUrl('/blank'); + advance(fixture); - const config = router.config as any; - const firstConfig = config[1]._loadedConfig !; + const config = router.config as any; + const firstConfig = config[1]._loadedConfig !; - expect(firstConfig).toBeDefined(); - expect(firstConfig.routes[0].path).toEqual('LoadedModule1'); + expect(firstConfig).toBeDefined(); + expect(firstConfig.routes[0].path).toEqual('LoadedModule1'); - const secondConfig = firstConfig.routes[0]._loadedConfig !; - expect(secondConfig).toBeDefined(); - expect(secondConfig.routes[0].path).toEqual('LoadedModule2'); - }))); + const secondConfig = firstConfig.routes[0]._loadedConfig !; + expect(secondConfig).toBeDefined(); + expect(secondConfig.routes[0].path).toEqual('LoadedModule2'); + }))); }); @@ -4097,69 +4087,71 @@ describe('Integration', () => { {providers: [{provide: UrlHandlingStrategy, useClass: CustomUrlHandlingStrategy}]}); }); - it('should work', - fakeAsync(inject([Router, Location], (router: Router, location: Location) => { - const fixture = createRoot(router, RootCmp); + fixmeIvy('unknown') && + it('should work', + fakeAsync(inject([Router, Location], (router: Router, location: Location) => { + const fixture = createRoot(router, RootCmp); - router.resetConfig([{ - path: 'include', - component: TeamCmp, - children: [ - {path: 'user/:name', component: UserCmp}, {path: 'simple', component: SimpleCmp} - ] - }]); + router.resetConfig([{ + path: 'include', + component: TeamCmp, + children: [ + {path: 'user/:name', component: UserCmp}, + {path: 'simple', component: SimpleCmp} + ] + }]); - const events: any[] = []; - router.events.subscribe(e => e instanceof RouterEvent && events.push(e)); + const events: any[] = []; + router.events.subscribe(e => e instanceof RouterEvent && events.push(e)); - // supported URL - router.navigateByUrl('/include/user/kate'); - advance(fixture); + // supported URL + router.navigateByUrl('/include/user/kate'); + advance(fixture); - expect(location.path()).toEqual('/include/user/kate'); - expectEvents(events, [ - [NavigationStart, '/include/user/kate'], [RoutesRecognized, '/include/user/kate'], - [GuardsCheckStart, '/include/user/kate'], [GuardsCheckEnd, '/include/user/kate'], - [ResolveStart, '/include/user/kate'], [ResolveEnd, '/include/user/kate'], - [NavigationEnd, '/include/user/kate'] - ]); - expect(fixture.nativeElement).toHaveText('team [ user kate, right: ]'); - events.splice(0); + expect(location.path()).toEqual('/include/user/kate'); + expectEvents(events, [ + [NavigationStart, '/include/user/kate'], [RoutesRecognized, '/include/user/kate'], + [GuardsCheckStart, '/include/user/kate'], [GuardsCheckEnd, '/include/user/kate'], + [ResolveStart, '/include/user/kate'], [ResolveEnd, '/include/user/kate'], + [NavigationEnd, '/include/user/kate'] + ]); + expect(fixture.nativeElement).toHaveText('team [ user kate, right: ]'); + events.splice(0); - // unsupported URL - router.navigateByUrl('/exclude/one'); - advance(fixture); + // unsupported URL + router.navigateByUrl('/exclude/one'); + advance(fixture); - expect(location.path()).toEqual('/exclude/one'); - expect(Object.keys(router.routerState.root.children).length).toEqual(0); - expect(fixture.nativeElement).toHaveText(''); - expectEvents(events, [ - [NavigationStart, '/exclude/one'], [GuardsCheckStart, '/exclude/one'], - [GuardsCheckEnd, '/exclude/one'], [NavigationEnd, '/exclude/one'] - ]); - events.splice(0); + expect(location.path()).toEqual('/exclude/one'); + expect(Object.keys(router.routerState.root.children).length).toEqual(0); + expect(fixture.nativeElement).toHaveText(''); + expectEvents(events, [ + [NavigationStart, '/exclude/one'], [GuardsCheckStart, '/exclude/one'], + [GuardsCheckEnd, '/exclude/one'], [NavigationEnd, '/exclude/one'] + ]); + events.splice(0); - // another unsupported URL - location.go('/exclude/two'); - advance(fixture); + // another unsupported URL + location.go('/exclude/two'); + advance(fixture); - expect(location.path()).toEqual('/exclude/two'); - expectEvents(events, []); + expect(location.path()).toEqual('/exclude/two'); + expectEvents(events, []); - // back to a supported URL - location.go('/include/simple'); - advance(fixture); + // back to a supported URL + location.go('/include/simple'); + advance(fixture); - expect(location.path()).toEqual('/include/simple'); - expect(fixture.nativeElement).toHaveText('team [ simple, right: ]'); + expect(location.path()).toEqual('/include/simple'); + expect(fixture.nativeElement).toHaveText('team [ simple, right: ]'); - expectEvents(events, [ - [NavigationStart, '/include/simple'], [RoutesRecognized, '/include/simple'], - [GuardsCheckStart, '/include/simple'], [GuardsCheckEnd, '/include/simple'], - [ResolveStart, '/include/simple'], [ResolveEnd, '/include/simple'], - [NavigationEnd, '/include/simple'] - ]); - }))); + expectEvents(events, [ + [NavigationStart, '/include/simple'], [RoutesRecognized, '/include/simple'], + [GuardsCheckStart, '/include/simple'], [GuardsCheckEnd, '/include/simple'], + [ResolveStart, '/include/simple'], [ResolveEnd, '/include/simple'], + [NavigationEnd, '/include/simple'] + ]); + }))); it('should handle the case when the router takes only the primary url', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { @@ -4219,43 +4211,41 @@ describe('Integration', () => { class LazyLoadedModule { } - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should not ignore empty path when in legacy mode', - fakeAsync(inject( - [Router, NgModuleFactoryLoader], - (router: Router, loader: SpyNgModuleFactoryLoader) => { - router.relativeLinkResolution = 'legacy'; - loader.stubbedModules = {expected: LazyLoadedModule}; + it('should not ignore empty path when in legacy mode', + fakeAsync(inject( + [Router, NgModuleFactoryLoader], + (router: Router, loader: SpyNgModuleFactoryLoader) => { + router.relativeLinkResolution = 'legacy'; + loader.stubbedModules = {expected: LazyLoadedModule}; - const fixture = createRoot(router, RootCmp); + const fixture = createRoot(router, RootCmp); - router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); + router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); - router.navigateByUrl('/lazy/foo/bar'); - advance(fixture); + router.navigateByUrl('/lazy/foo/bar'); + advance(fixture); - const link = fixture.nativeElement.querySelector('a'); - expect(link.getAttribute('href')).toEqual('/lazy/foo/bar/simple'); - }))); + const link = fixture.nativeElement.querySelector('a'); + expect(link.getAttribute('href')).toEqual('/lazy/foo/bar/simple'); + }))); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should ignore empty path when in corrected mode', - fakeAsync(inject( - [Router, NgModuleFactoryLoader], - (router: Router, loader: SpyNgModuleFactoryLoader) => { - router.relativeLinkResolution = 'corrected'; - loader.stubbedModules = {expected: LazyLoadedModule}; + it('should ignore empty path when in corrected mode', + fakeAsync(inject( + [Router, NgModuleFactoryLoader], + (router: Router, loader: SpyNgModuleFactoryLoader) => { + router.relativeLinkResolution = 'corrected'; + loader.stubbedModules = {expected: LazyLoadedModule}; - const fixture = createRoot(router, RootCmp); + const fixture = createRoot(router, RootCmp); - router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); + router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); - router.navigateByUrl('/lazy/foo/bar'); - advance(fixture); + router.navigateByUrl('/lazy/foo/bar'); + advance(fixture); - const link = fixture.nativeElement.querySelector('a'); - expect(link.getAttribute('href')).toEqual('/lazy/foo/simple'); - }))); + const link = fixture.nativeElement.querySelector('a'); + expect(link.getAttribute('href')).toEqual('/lazy/foo/simple'); + }))); }); }); diff --git a/packages/router/test/router_preloader.spec.ts b/packages/router/test/router_preloader.spec.ts index 5b65c9eaca..ca2e500cbd 100644 --- a/packages/router/test/router_preloader.spec.ts +++ b/packages/router/test/router_preloader.spec.ts @@ -36,20 +36,20 @@ describe('RouterPreloader', () => { }); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should work', - fakeAsync(inject( - [NgModuleFactoryLoader, RouterPreloader, Router], - (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router) => { - loader.stubbedModules = {expected: LoadedModule}; - preloader.preload().subscribe(() => {}); + it('should work', + fakeAsync(inject( + [NgModuleFactoryLoader, RouterPreloader, Router], + (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router) => { + loader.stubbedModules = {expected: LoadedModule}; - tick(); + preloader.preload().subscribe(() => {}); - const c = router.config; - expect((c[0] as any)._loadedConfig).not.toBeDefined(); - }))); + tick(); + + const c = router.config; + expect((c[0] as any)._loadedConfig).not.toBeDefined(); + }))); }); describe('should preload configurations', () => { @@ -60,7 +60,8 @@ describe('RouterPreloader', () => { }); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && + + fixmeIvy('unknown') && it('should work', fakeAsync(inject( [NgModuleFactoryLoader, RouterPreloader, Router, NgModuleRef], @@ -200,21 +201,21 @@ describe('RouterPreloader', () => { }); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should work', - fakeAsync(inject( - [NgModuleFactoryLoader, RouterPreloader, Router], - (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router) => { - loader.stubbedModules = {expected2: LoadedModule}; - preloader.preload().subscribe(() => {}); + it('should work', + fakeAsync(inject( + [NgModuleFactoryLoader, RouterPreloader, Router], + (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router) => { + loader.stubbedModules = {expected2: LoadedModule}; - tick(); + preloader.preload().subscribe(() => {}); - const c = router.config; - expect((c[0] as any)._loadedConfig).not.toBeDefined(); - expect((c[1] as any)._loadedConfig).toBeDefined(); - }))); + tick(); + + const c = router.config; + expect((c[0] as any)._loadedConfig).not.toBeDefined(); + expect((c[1] as any)._loadedConfig).toBeDefined(); + }))); }); describe('should copy loaded configs', () => { @@ -230,22 +231,22 @@ describe('RouterPreloader', () => { }); }); - fixmeIvy('FW-561: Runtime compiler is not loaded') && - it('should work', - fakeAsync(inject( - [NgModuleFactoryLoader, RouterPreloader, Router], - (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router) => { - loader.stubbedModules = {expected: LoadedModule}; - preloader.preload().subscribe(() => {}); + it('should work', + fakeAsync(inject( + [NgModuleFactoryLoader, RouterPreloader, Router], + (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router) => { + loader.stubbedModules = {expected: LoadedModule}; - tick(); + preloader.preload().subscribe(() => {}); - const c = router.config as{_loadedConfig: LoadedRouterConfig}[]; - expect(c[0]._loadedConfig).toBeDefined(); - expect(c[0]._loadedConfig !.routes).not.toBe(configs); - expect(c[0]._loadedConfig !.routes[0]).not.toBe(configs[0]); - expect(c[0]._loadedConfig !.routes[0].component).toBe(configs[0].component); - }))); + tick(); + + const c = router.config as{_loadedConfig: LoadedRouterConfig}[]; + expect(c[0]._loadedConfig).toBeDefined(); + expect(c[0]._loadedConfig !.routes).not.toBe(configs); + expect(c[0]._loadedConfig !.routes[0]).not.toBe(configs[0]); + expect(c[0]._loadedConfig !.routes[0].component).toBe(configs[0].component); + }))); }); }); diff --git a/tools/public_api_guard/core/core.d.ts b/tools/public_api_guard/core/core.d.ts index 60122730f9..6524dde4a5 100644 --- a/tools/public_api_guard/core/core.d.ts +++ b/tools/public_api_guard/core/core.d.ts @@ -76,12 +76,12 @@ export interface CollectionChangeRecord extends IterableChangeRecord { } export declare class Compiler { + compileModuleAndAllComponentsAsync: (moduleType: Type) => Promise>; + compileModuleAndAllComponentsSync: (moduleType: Type) => ModuleWithComponentFactories; + compileModuleAsync: (moduleType: Type) => Promise>; + compileModuleSync: (moduleType: Type) => NgModuleFactory; clearCache(): void; clearCacheFor(type: Type): void; - compileModuleAndAllComponentsAsync(moduleType: Type): Promise>; - compileModuleAndAllComponentsSync(moduleType: Type): ModuleWithComponentFactories; - compileModuleAsync(moduleType: Type): Promise>; - compileModuleSync(moduleType: Type): NgModuleFactory; getModuleId(moduleType: Type): string | undefined; } @@ -188,48 +188,50 @@ export declare function createPlatformFactory(parentPlatformFactory: ((extraProv export declare const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata; -export declare class DebugElement extends DebugNode { - attributes: { +export interface DebugElement extends DebugNode { + readonly attributes: { [key: string]: string | null; }; - childNodes: DebugNode[]; + readonly childNodes: DebugNode[]; readonly children: DebugElement[]; - classes: { + readonly classes: { [key: string]: boolean; }; - name: string; - nativeElement: any; - properties: { + readonly name: string; + readonly nativeElement: any; + readonly properties: { [key: string]: any; }; - styles: { + readonly styles: { [key: string]: string | null; }; - constructor(nativeNode: any, parent: any, _debugContext: DebugContext); - addChild(child: DebugNode): void; - insertBefore(refChild: DebugNode, newChild: DebugNode): void; - insertChildrenAfter(child: DebugNode, newChildren: DebugNode[]): void; query(predicate: Predicate): DebugElement; queryAll(predicate: Predicate): DebugElement[]; queryAllNodes(predicate: Predicate): DebugNode[]; - removeChild(child: DebugNode): void; triggerEventHandler(eventName: string, eventObj: any): void; } -export declare class DebugNode { +export declare const DebugElement: { + new (...args: any[]): DebugElement; +}; + +export interface DebugNode { readonly componentInstance: any; readonly context: any; readonly injector: Injector; - listeners: EventListener[]; - nativeNode: any; - parent: DebugElement | null; + readonly listeners: EventListener[]; + readonly nativeNode: any; + readonly parent: DebugElement | null; readonly providerTokens: any[]; readonly references: { [key: string]: any; }; - constructor(nativeNode: any, parent: DebugNode | null, _debugContext: DebugContext); } +export declare const DebugNode: { + new (...args: any[]): DebugNode; +}; + /** @deprecated */ export declare class DefaultIterableDiffer implements IterableDiffer, IterableChanges { readonly collection: V[] | Iterable | null; @@ -330,7 +332,7 @@ export interface ForwardRefFn { (): any; } -export declare function getDebugNode(nativeNode: any): DebugNode | null; +export declare const getDebugNode: (nativeNode: any) => DebugNode | null; export declare function getModuleFactory(id: string): NgModuleFactory;