| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-03 11:42:48 +01:00
										 |  |  | import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; | 
					
						
							| 
									
										
										
										
											2018-01-09 16:43:12 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-06 11:58:08 -08:00
										 |  |  | import {CreateComponentOptions} from '../../src/render3/component'; | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  | import {ComponentTemplate, ComponentType, DirectiveType, PublicFeature, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index'; | 
					
						
							| 
									
										
										
										
											2018-01-08 20:17:13 -08:00
										 |  |  | import {NG_HOST_SYMBOL, createLNode, createLView, renderTemplate} from '../../src/render3/instructions'; | 
					
						
							| 
									
										
										
										
											2018-01-09 18:38:17 -08:00
										 |  |  | import {LElementNode, LNodeFlags} from '../../src/render3/interfaces/node'; | 
					
						
							|  |  |  | import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; | 
					
						
							| 
									
										
										
										
											2018-01-09 16:43:12 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-11 16:30:46 +01:00
										 |  |  | import {getRendererFactory2} from './imported_renderer2'; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  | export abstract class BaseFixture { | 
					
						
							|  |  |  |   hostElement: HTMLElement; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   constructor() { | 
					
						
							|  |  |  |     this.hostElement = document.createElement('div'); | 
					
						
							|  |  |  |     this.hostElement.setAttribute('fixture', 'mark'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Current state of rendered HTML. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   get html(): string { | 
					
						
							|  |  |  |     return (this.hostElement as any as Element) | 
					
						
							|  |  |  |         .innerHTML.replace(/ style=""/g, '') | 
					
						
							|  |  |  |         .replace(/ class=""/g, ''); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-01 17:14:01 -08:00
										 |  |  | function noop() {} | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Fixture for testing template functions in a convenient way. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This fixture allows: | 
					
						
							|  |  |  |  * - specifying the creation block and update block as two separate functions, | 
					
						
							|  |  |  |  * - maintaining the template state between invocations, | 
					
						
							|  |  |  |  * - access to the render `html`. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  | export class TemplateFixture extends BaseFixture { | 
					
						
							| 
									
										
										
										
											2018-03-01 17:14:01 -08:00
										 |  |  |   hostNode: LElementNode; | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * @param createBlock Instructions which go into the creation block: | 
					
						
							|  |  |  |    *          `if (creationMode) { __here__ }`. | 
					
						
							|  |  |  |    * @param updateBlock Optional instructions which go after the creation block: | 
					
						
							|  |  |  |    *          `if (creationMode) { ... } __here__`. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   constructor(private createBlock: () => void, private updateBlock: () => void = noop) { | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  |     super(); | 
					
						
							| 
									
										
										
										
											2018-03-01 17:14:01 -08:00
										 |  |  |     this.updateBlock = updateBlock || function() {}; | 
					
						
							|  |  |  |     this.hostNode = renderTemplate(this.hostElement, (ctx: any, cm: boolean) => { | 
					
						
							|  |  |  |       if (cm) { | 
					
						
							|  |  |  |         this.createBlock(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       this.updateBlock(); | 
					
						
							|  |  |  |     }, null !, domRendererFactory3, null); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Update the existing template | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * @param updateBlock Optional update block. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   update(updateBlock?: () => void): void { | 
					
						
							|  |  |  |     renderTemplate( | 
					
						
							|  |  |  |         this.hostNode.native, updateBlock || this.updateBlock, null !, domRendererFactory3, | 
					
						
							|  |  |  |         this.hostNode); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-01 17:14:01 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Fixture for testing Components in a convenient way. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export class ComponentFixture<T> extends BaseFixture { | 
					
						
							|  |  |  |   component: T; | 
					
						
							|  |  |  |   requestAnimationFrame: {(fn: () => void): void; flush(): void; queue: (() => void)[];}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   constructor(private componentType: ComponentType<T>) { | 
					
						
							|  |  |  |     super(); | 
					
						
							|  |  |  |     this.requestAnimationFrame = function(fn: () => void) { | 
					
						
							|  |  |  |       requestAnimationFrame.queue.push(fn); | 
					
						
							|  |  |  |     } as any; | 
					
						
							|  |  |  |     this.requestAnimationFrame.queue = []; | 
					
						
							|  |  |  |     this.requestAnimationFrame.flush = function() { | 
					
						
							|  |  |  |       while (requestAnimationFrame.queue.length) { | 
					
						
							|  |  |  |         requestAnimationFrame.queue.shift() !(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.component = _renderComponent(componentType, { | 
					
						
							|  |  |  |       host: this.hostElement, | 
					
						
							|  |  |  |       scheduler: this.requestAnimationFrame, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   update(): void { | 
					
						
							|  |  |  |     tick(this.component); | 
					
						
							|  |  |  |     this.requestAnimationFrame.flush(); | 
					
						
							| 
									
										
										
										
											2018-03-01 17:14:01 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | // The methods below use global state and we should stop using them.
 | 
					
						
							|  |  |  | // Fixtures above are preferred way of testing Components and Templates
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2018-03-01 17:14:01 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | export const document = ((global || window) as any).document; | 
					
						
							|  |  |  | export let containerEl: HTMLElement = null !; | 
					
						
							| 
									
										
										
										
											2018-01-08 20:17:13 -08:00
										 |  |  | let host: LElementNode|null; | 
					
						
							| 
									
										
										
										
											2017-12-27 10:54:19 -08:00
										 |  |  | const isRenderer2 = | 
					
						
							|  |  |  |     typeof process == 'object' && process.argv[3] && process.argv[3] === '--r=renderer2'; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | // tslint:disable-next-line:no-console
 | 
					
						
							| 
									
										
										
										
											2017-12-11 16:30:46 +01:00
										 |  |  | console.log(`Running tests with ${!isRenderer2 ? 'document' : 'Renderer2'} renderer...`); | 
					
						
							|  |  |  | const testRendererFactory: RendererFactory3 = | 
					
						
							|  |  |  |     isRenderer2 ? getRendererFactory2(document) : domRendererFactory3; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const requestAnimationFrame: | 
					
						
							|  |  |  |     {(fn: () => void): void; flush(): void; queue: (() => void)[];} = function(fn: () => void) { | 
					
						
							|  |  |  |       requestAnimationFrame.queue.push(fn); | 
					
						
							|  |  |  |     } as any; | 
					
						
							|  |  |  | requestAnimationFrame.flush = function() { | 
					
						
							|  |  |  |   while (requestAnimationFrame.queue.length) { | 
					
						
							|  |  |  |     requestAnimationFrame.queue.shift() !(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function resetDOM() { | 
					
						
							|  |  |  |   requestAnimationFrame.queue = []; | 
					
						
							| 
									
										
										
										
											2017-12-15 17:50:54 +01:00
										 |  |  |   if (containerEl) { | 
					
						
							| 
									
										
										
										
											2018-01-03 10:45:09 +01:00
										 |  |  |     try { | 
					
						
							|  |  |  |       document.body.removeChild(containerEl); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-12-15 17:50:54 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   containerEl = document.createElement('div'); | 
					
						
							|  |  |  |   containerEl.setAttribute('host', ''); | 
					
						
							| 
									
										
										
										
											2017-12-15 17:50:54 +01:00
										 |  |  |   document.body.appendChild(containerEl); | 
					
						
							| 
									
										
										
										
											2017-12-11 16:30:46 +01:00
										 |  |  |   host = null; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   // TODO: assert that the global state is clean (e.g. ngData, previousOrParentNode, etc)
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @deprecated use `TemplateFixture` or `ComponentFixture` | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-12-11 16:30:46 +01:00
										 |  |  | export function renderToHtml( | 
					
						
							|  |  |  |     template: ComponentTemplate<any>, ctx: any, providedRendererFactory?: RendererFactory3) { | 
					
						
							|  |  |  |   host = renderTemplate( | 
					
						
							|  |  |  |       containerEl, template, ctx, providedRendererFactory || testRendererFactory, host); | 
					
						
							|  |  |  |   return toHtml(containerEl); | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | beforeEach(resetDOM); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @deprecated use `TemplateFixture` or `ComponentFixture` | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-03-06 10:13:49 -08:00
										 |  |  | export function renderComponent<T>(type: ComponentType<T>, opts?: CreateComponentOptions): T { | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  |   return _renderComponent(type, { | 
					
						
							| 
									
										
										
										
											2018-03-06 10:13:49 -08:00
										 |  |  |     rendererFactory: opts && opts.rendererFactory || testRendererFactory, | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  |     host: containerEl, | 
					
						
							|  |  |  |     scheduler: requestAnimationFrame, | 
					
						
							| 
									
										
										
										
											2018-03-06 11:58:08 -08:00
										 |  |  |     hostFeatures: opts && opts.hostFeatures | 
					
						
							| 
									
										
										
										
											2018-02-03 20:34:30 -08:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-13 12:31:21 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @deprecated use `TemplateFixture` or `ComponentFixture` | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | export function toHtml<T>(componentOrElement: T | RElement): string { | 
					
						
							| 
									
										
										
										
											2018-01-08 20:17:13 -08:00
										 |  |  |   const node = (componentOrElement as any)[NG_HOST_SYMBOL] as LElementNode; | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   if (node) { | 
					
						
							|  |  |  |     return toHtml(node.native); | 
					
						
							|  |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2018-01-03 11:42:48 +01:00
										 |  |  |     return stringifyElement(componentOrElement) | 
					
						
							|  |  |  |         .replace(/^<div host="">/, '') | 
					
						
							|  |  |  |         .replace(/<\/div>$/, '') | 
					
						
							|  |  |  |         .replace(' style=""', '') | 
					
						
							|  |  |  |         .replace(/<!--[\w]*-->/g, ''); | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function createComponent( | 
					
						
							|  |  |  |     name: string, template: ComponentTemplate<any>): ComponentType<any> { | 
					
						
							|  |  |  |   return class Component { | 
					
						
							|  |  |  |     value: any; | 
					
						
							| 
									
										
										
										
											2018-01-22 15:27:21 -08:00
										 |  |  |     static ngComponentDef = defineComponent({ | 
					
						
							|  |  |  |       type: Component, | 
					
						
							|  |  |  |       tag: name, | 
					
						
							|  |  |  |       factory: () => new Component, | 
					
						
							|  |  |  |       template: template, | 
					
						
							|  |  |  |       features: [PublicFeature] | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-08 21:57:50 -08:00
										 |  |  | export function createDirective({exportAs}: {exportAs?: string} = {}): DirectiveType<any> { | 
					
						
							| 
									
										
										
										
											2017-12-19 16:51:42 +01:00
										 |  |  |   return class Directive { | 
					
						
							|  |  |  |     static ngDirectiveDef = defineDirective({ | 
					
						
							| 
									
										
										
										
											2018-01-22 15:27:21 -08:00
										 |  |  |       type: Directive, | 
					
						
							| 
									
										
										
										
											2017-12-19 16:51:42 +01:00
										 |  |  |       factory: () => new Directive(), | 
					
						
							| 
									
										
										
										
											2017-12-20 12:19:59 +01:00
										 |  |  |       features: [PublicFeature], | 
					
						
							| 
									
										
										
										
											2018-01-08 21:57:50 -08:00
										 |  |  |       exportAs: exportAs, | 
					
						
							| 
									
										
										
										
											2017-12-19 16:51:42 +01:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-12-01 14:23:03 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Verify that DOM is a type of render. This is here for error checking only and has no use.
 | 
					
						
							|  |  |  | export const renderer: Renderer3 = null as any as Document; | 
					
						
							|  |  |  | export const element: RElement = null as any as HTMLElement; | 
					
						
							|  |  |  | export const text: RText = null as any as Text; |