From ae19d071bb7e467bd4f8acd31bdfd062d5575004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Tue, 13 Mar 2018 12:31:21 -0700 Subject: [PATCH] test(ivy): add ComponentFixture for cleaner tests. (#22719) PR Close #22719 --- packages/core/src/render3/index.ts | 1 + packages/core/test/render3/render_util.ts | 75 +++++++++++++++++++---- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 2531aea146..13d35a703a 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -65,6 +65,7 @@ export { embeddedViewEnd as v, detectChanges, markDirty, + tick, } from './instructions'; export { diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 56e82b0d48..153bf33d23 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -9,7 +9,7 @@ import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; import {CreateComponentOptions} from '../../src/render3/component'; -import {ComponentTemplate, ComponentType, DirectiveType, PublicFeature, defineComponent, defineDirective, renderComponent as _renderComponent} from '../../src/render3/index'; +import {ComponentTemplate, ComponentType, DirectiveType, PublicFeature, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index'; import {NG_HOST_SYMBOL, createLNode, createLView, renderTemplate} from '../../src/render3/instructions'; import {DirectiveDefArgs} from '../../src/render3/interfaces/definition'; import {LElementNode, LNodeFlags} from '../../src/render3/interfaces/node'; @@ -17,6 +17,24 @@ import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from import {getRendererFactory2} from './imported_renderer2'; +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, ''); + } +} + function noop() {} /** * Fixture for testing template functions in a convenient way. @@ -26,11 +44,8 @@ function noop() {} * - maintaining the template state between invocations, * - access to the render `html`. */ -export class TemplateFixture { - hostElement: HTMLElement; - +export class TemplateFixture extends BaseFixture { hostNode: LElementNode; - /** * * @param createBlock Instructions which go into the creation block: @@ -39,8 +54,8 @@ export class TemplateFixture { * `if (creationMode) { ... } __here__`. */ constructor(private createBlock: () => void, private updateBlock: () => void = noop) { + super(); this.updateBlock = updateBlock || function() {}; - this.hostElement = document.createElement('div'); this.hostNode = renderTemplate(this.hostElement, (ctx: any, cm: boolean) => { if (cm) { this.createBlock(); @@ -59,15 +74,44 @@ export class TemplateFixture { this.hostNode.native, updateBlock || this.updateBlock, null !, domRendererFactory3, this.hostNode); } +} - /** - * Current state of rendered HTML. - */ - get html(): string { - return (this.hostNode.native as any as Element).innerHTML.replace(/ style=""/g, ''); + +/** + * Fixture for testing Components in a convenient way. + */ +export class ComponentFixture extends BaseFixture { + component: T; + requestAnimationFrame: {(fn: () => void): void; flush(): void; queue: (() => void)[];}; + + constructor(private componentType: ComponentType) { + 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(); } } +/////////////////////////////////////////////////////////////////////////////////// +// The methods below use global state and we should stop using them. +// Fixtures above are preferred way of testing Components and Templates +/////////////////////////////////////////////////////////////////////////////////// export const document = ((global || window) as any).document; export let containerEl: HTMLElement = null !; @@ -104,6 +148,9 @@ export function resetDOM() { // TODO: assert that the global state is clean (e.g. ngData, previousOrParentNode, etc) } +/** + * @deprecated use `TemplateFixture` or `ComponentFixture` + */ export function renderToHtml( template: ComponentTemplate, ctx: any, providedRendererFactory?: RendererFactory3) { host = renderTemplate( @@ -113,6 +160,9 @@ export function renderToHtml( beforeEach(resetDOM); +/** + * @deprecated use `TemplateFixture` or `ComponentFixture` + */ export function renderComponent(type: ComponentType, opts?: CreateComponentOptions): T { return _renderComponent(type, { rendererFactory: opts && opts.rendererFactory || testRendererFactory, @@ -122,6 +172,9 @@ export function renderComponent(type: ComponentType, opts?: CreateComponen }); } +/** + * @deprecated use `TemplateFixture` or `ComponentFixture` + */ export function toHtml(componentOrElement: T | RElement): string { const node = (componentOrElement as any)[NG_HOST_SYMBOL] as LElementNode; if (node) {