diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index 02c0279e1..72a14cead 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -1,42 +1,19 @@ //make sure you import mocha-config before angular2/core import {assert} from "./test-config"; -import {bootstrap} from "../nativescript-angular/application"; import { - Type, Component, - ComponentRef, - DynamicComponentLoader, - ViewChild, ElementRef, - provide } from "angular2/core"; -import {View} from "ui/core/view"; -import * as background from "ui/styling/background"; -import {StackLayout} from "ui/layouts/stack-layout"; -import {GridLayout} from "ui/layouts/grid-layout"; -import {LayoutBase} from "ui/layouts/layout-base"; import {ProxyViewContainer} from "ui/proxy-view-container"; -import {topmost} from 'ui/frame'; -import {APP_ROOT_VIEW} from "../nativescript-angular/platform-providers"; import {Red} from "color/known-colors"; - -@Component({ - selector: 'my-app', - template: `` -}) -export class App { - @ViewChild("loadSite") public loadSiteRef: ElementRef; - - constructor(public loader: DynamicComponentLoader, - public elementRef: ElementRef) { - } -} +import {dumpView} from "./test-utils"; +import {TestApp} from "./test-app"; @Component({ template: `` }) export class LayoutWithLabel { - constructor(public elementRef: ElementRef){} + constructor(public elementRef: ElementRef) { } } @Component({ @@ -44,7 +21,7 @@ export class LayoutWithLabel { template: `` }) export class LabelCmp { - constructor(public elementRef: ElementRef){ + constructor(public elementRef: ElementRef) { } } @@ -53,7 +30,7 @@ export class LabelCmp { template: `` }) export class LabelContainer { - constructor(public elementRef: ElementRef){} + constructor(public elementRef: ElementRef) { } } @Component({ @@ -61,7 +38,7 @@ export class LabelContainer { template: `` }) export class ProjectableCmp { - constructor(public elementRef: ElementRef){ + constructor(public elementRef: ElementRef) { } } @Component({ @@ -71,7 +48,7 @@ export class ProjectableCmp { ` }) export class ProjectionContainer { - constructor(public elementRef: ElementRef){} + constructor(public elementRef: ElementRef) { } } @Component({ @@ -82,90 +59,125 @@ export class ProjectionContainer { template: `` }) export class StyledLabelCmp { - constructor(public elementRef: ElementRef){ + constructor(public elementRef: ElementRef) { } } -describe('Renderer E2E', () => { - let appComponent: App = null; - let _pendingDispose: ComponentRef[] = []; +@Component({ + selector: "ng-if-label", + template: `` +}) +export class NgIfLabel { + public show: boolean = false; + constructor(public elementRef: ElementRef) { + } +} - function loadComponent(type: Type): Promise { - return appComponent.loader.loadIntoLocation(type, appComponent.elementRef, "loadSite").then((componentRef) => { - _pendingDispose.push(componentRef); - return componentRef; - }); +@Component({ + selector: "ng-for-label", + template: `` +}) +export class NgForLabel { + public items: Array = ["one", "two", "three"]; + constructor(public elementRef: ElementRef) { } +} - afterEach(() => { - while (_pendingDispose.length > 0) { - const componentRef = _pendingDispose.pop() - componentRef.dispose(); - } - }); + +describe('Renderer E2E', () => { + let testApp: TestApp = null; before(() => { - //bootstrap the app in a custom location - const page = topmost().currentPage; - const rootLayout = page.content; - const viewRoot = new StackLayout(); - rootLayout.addChild(viewRoot); - GridLayout.setRow(rootLayout, 50); - const rootViewProvider = provide(APP_ROOT_VIEW, {useFactory: () => viewRoot}); - return bootstrap(App, [rootViewProvider]).then((componentRef) => { - appComponent = componentRef.instance; - }); + return TestApp.create().then((app) => { + testApp = app; + }) + }); + + after(() => { + testApp.dispose(); + }); + + afterEach(() => { + testApp.disposeComponenets(); }); it("component with a layout", () => { - return loadComponent(LayoutWithLabel).then((componentRef) => { + return testApp.loadComponent(LayoutWithLabel).then((componentRef) => { const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer (StackLayout (Label)))", dumpView(componentRoot)); }); }); it("component without a layout", () => { - return loadComponent(LabelContainer).then((componentRef) => { + return testApp.loadComponent(LabelContainer).then((componentRef) => { const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer (GridLayout (ProxyViewContainer (Label))))", dumpView(componentRoot)); }); }); it("projects content into components", () => { - return loadComponent(ProjectionContainer).then((componentRef) => { + return testApp.loadComponent(ProjectionContainer).then((componentRef) => { const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer (GridLayout (ProxyViewContainer (StackLayout (Button)))))", dumpView(componentRoot)); }); }); it("applies component styles", () => { - return loadComponent(StyledLabelCmp).then((componentRef) => { + return testApp.loadComponent(StyledLabelCmp).then((componentRef) => { const componentRoot = componentRef.instance.elementRef.nativeElement; const label = (componentRoot).getChildAt(0); assert.equal(Red, label.style.color.hex); }); }); -}); + describe("Structural directives", () => { + it("ngIf hides component when false", () => { + return testApp.loadComponent(NgIfLabel).then((componentRef) => { + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.equal("(ProxyViewContainer (template))", dumpView(componentRoot)); + }); + }); -function dumpView(view: View): string { - let nodeName = (view).nodeName - if (!nodeName) { - nodeName = (view.constructor).name + '!'; - } - let output = ["(", nodeName, " "]; - (view)._eachChildView((child) => { - const childDump = dumpView(child); - output.push(childDump); - output.push(", "); - return true; - }); - if (output[output.length - 1] == ", ") { - output.pop(); - } - if (output[output.length - 1] == " ") { - output.pop(); - } - output.push(")"); - return output.join(""); -} + it("ngIf show component when true", () => { + return testApp.loadComponent(NgIfLabel).then((componentRef) => { + const component = componentRef.instance; + const componentRoot = component.elementRef.nativeElement; + + component.show = true; + testApp.appRef.tick(); + assert.equal("(ProxyViewContainer (template), (Label))", dumpView(componentRoot)); + }); + }) + + it("ngFor creates element for each item", () => { + return testApp.loadComponent(NgForLabel).then((componentRef) => { + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.equal("(ProxyViewContainer (template), (Label[text=one]), (Label[text=two]), (Label[text=three]))", dumpView(componentRoot, true)); + }); + }); + + it("ngFor updates when item is removed", () => { + return testApp.loadComponent(NgForLabel).then((componentRef) => { + const component = componentRef.instance; + const componentRoot = component.elementRef.nativeElement; + + component.items.splice(1, 1); + testApp.appRef.tick(); + + assert.equal("(ProxyViewContainer (template), (Label[text=one]), (Label[text=three]))", dumpView(componentRoot, true)); + }); + }); + + it("ngFor updates when item is inserted", () => { + return testApp.loadComponent(NgForLabel).then((componentRef) => { + const component = componentRef.instance; + const componentRoot = component.elementRef.nativeElement; + + component.items.splice(1, 0, "new"); + testApp.appRef.tick(); + + assert.equal("(ProxyViewContainer (template), (Label[text=one]), (Label[text=new]), (Label[text=two]), (Label[text=three]))", dumpView(componentRoot, true)); + }); + }); + }) +}) \ No newline at end of file diff --git a/tests/app/tests/test-app.ts b/tests/app/tests/test-app.ts new file mode 100644 index 000000000..477517ac1 --- /dev/null +++ b/tests/app/tests/test-app.ts @@ -0,0 +1,75 @@ +//make sure you import mocha-config before angular2/core +import {bootstrap} from "../nativescript-angular/application"; +import { + Type, + Component, + ComponentRef, + DynamicComponentLoader, + ViewChild, + ElementRef, + provide, + ApplicationRef +} from "angular2/core"; + +import {View} from "ui/core/view"; +import {StackLayout} from "ui/layouts/stack-layout"; +import {GridLayout} from "ui/layouts/grid-layout"; +import {LayoutBase} from "ui/layouts/layout-base"; +import {topmost} from 'ui/frame'; +import {APP_ROOT_VIEW} from "../nativescript-angular/platform-providers"; + +@Component({ + selector: 'my-app', + template: `` +}) +export class TestApp { + @ViewChild("loadSite") public loadSiteRef: ElementRef; + private _pageRoot: LayoutBase; + private _appRoot: StackLayout; + private _pendingDispose: ComponentRef[] = []; + + constructor(public loader: DynamicComponentLoader, + public elementRef: ElementRef, + public appRef: ApplicationRef) { + } + + public loadComponent(type: Type): Promise { + return this.loader.loadIntoLocation(type, this.elementRef, "loadSite").then((componentRef) => { + this._pendingDispose.push(componentRef); + this.appRef.tick(); + return componentRef; + }); + } + + public disposeComponenets() { + while (this._pendingDispose.length > 0) { + const componentRef = this._pendingDispose.pop() + componentRef.dispose(); + } + } + + public static create(): Promise { + const page = topmost().currentPage; + const rootLayout = page.content; + const viewRoot = new StackLayout(); + rootLayout.addChild(viewRoot); + GridLayout.setRow(rootLayout, 50); + const rootViewProvider = provide(APP_ROOT_VIEW, { useFactory: () => viewRoot }); + return bootstrap(TestApp, [rootViewProvider]).then((componentRef) => { + const testApp = componentRef.instance; + testApp._pageRoot = rootLayout; + testApp._appRoot = viewRoot; + return testApp; + }); + } + + public dispose() { + if (!this._appRoot) { + throw new Error("Test app already disposed or not initalized."); + } + this.disposeComponenets(); + this._pageRoot.removeChild(this._appRoot); + this._appRoot = null; + this._pageRoot = null; + } +} \ No newline at end of file diff --git a/tests/app/tests/test-utils.ts b/tests/app/tests/test-utils.ts new file mode 100644 index 000000000..37f32bc5c --- /dev/null +++ b/tests/app/tests/test-utils.ts @@ -0,0 +1,29 @@ +import {View} from "ui/core/view"; +import {TextBase} from "ui/text-base"; + +function getChildren(view: View): Array { + var children: Array = []; + (view)._eachChildView((child) => { + children.push(child); + return true; + }); + return children; +} + +export function dumpView(view: View, verbose: boolean = false): string { + let nodeName = (view).nodeName + let output = ["(", nodeName]; + if (verbose) { + if (view instanceof TextBase) { + output.push("[text=", view.text, "]") + } + } + + let children = getChildren(view).map((c) => dumpView(c, verbose)).join(", "); + if (children) { + output.push(" ", children); + } + + output.push(")"); + return output.join(""); +}