From 1c27b1b90b6c5ee922664fdd3076316b58f6794c Mon Sep 17 00:00:00 2001 From: vakrilov Date: Fri, 11 Mar 2016 18:23:44 +0200 Subject: [PATCH 1/2] ngIf tests --- tests/app/tests/renderer-tests.ts | 53 +++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index 02c0279e1..0825d95d6 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -8,7 +8,8 @@ import { DynamicComponentLoader, ViewChild, ElementRef, - provide + provide, + ApplicationRef } from "angular2/core"; import {View} from "ui/core/view"; import * as background from "ui/styling/background"; @@ -28,7 +29,8 @@ export class App { @ViewChild("loadSite") public loadSiteRef: ElementRef; constructor(public loader: DynamicComponentLoader, - public elementRef: ElementRef) { + public elementRef: ElementRef, + public appRef: ApplicationRef) { } } @@ -36,7 +38,7 @@ export class App { template: `` }) export class LayoutWithLabel { - constructor(public elementRef: ElementRef){} + constructor(public elementRef: ElementRef) { } } @Component({ @@ -44,7 +46,7 @@ export class LayoutWithLabel { template: `` }) export class LabelCmp { - constructor(public elementRef: ElementRef){ + constructor(public elementRef: ElementRef) { } } @@ -53,7 +55,7 @@ export class LabelCmp { template: `` }) export class LabelContainer { - constructor(public elementRef: ElementRef){} + constructor(public elementRef: ElementRef) { } } @Component({ @@ -61,7 +63,7 @@ export class LabelContainer { template: `` }) export class ProjectableCmp { - constructor(public elementRef: ElementRef){ + constructor(public elementRef: ElementRef) { } } @Component({ @@ -71,7 +73,7 @@ export class ProjectableCmp { ` }) export class ProjectionContainer { - constructor(public elementRef: ElementRef){} + constructor(public elementRef: ElementRef) { } } @Component({ @@ -82,13 +84,25 @@ export class ProjectionContainer { template: `` }) export class StyledLabelCmp { - constructor(public elementRef: ElementRef){ + constructor(public elementRef: ElementRef) { } } +@Component({ + selector: "conditional-label", + template: `` +}) +export class ConditionalLabel { + public show: boolean = false; + constructor(public elementRef: ElementRef) { + } +} + + describe('Renderer E2E', () => { let appComponent: App = null; let _pendingDispose: ComponentRef[] = []; + let appRef: ApplicationRef = null; function loadComponent(type: Type): Promise { return appComponent.loader.loadIntoLocation(type, appComponent.elementRef, "loadSite").then((componentRef) => { @@ -111,9 +125,10 @@ describe('Renderer E2E', () => { const viewRoot = new StackLayout(); rootLayout.addChild(viewRoot); GridLayout.setRow(rootLayout, 50); - const rootViewProvider = provide(APP_ROOT_VIEW, {useFactory: () => viewRoot}); + const rootViewProvider = provide(APP_ROOT_VIEW, { useFactory: () => viewRoot }); return bootstrap(App, [rootViewProvider]).then((componentRef) => { appComponent = componentRef.instance; + appRef = appComponent.appRef; }); }); @@ -146,6 +161,26 @@ describe('Renderer E2E', () => { }); }); + describe("Structural directives", () => { + it("ngIf hides component when false", () => { + return loadComponent(ConditionalLabel).then((componentRef) => { + const componentRoot = componentRef.instance.elementRef.nativeElement; + assert.equal("(ProxyViewContainer (template))", dumpView(componentRoot)); + }); + }); + + it("ngIf show component when true", () => { + return loadComponent(ConditionalLabel).then((componentRef) => { + const component = componentRef.instance; + const componentRoot = component.elementRef.nativeElement; + + component.show = true; + appRef.tick(); + assert.equal("(ProxyViewContainer (template), (Label))", dumpView(componentRoot)); + }); + }) + }) + }); function dumpView(view: View): string { From 42bbb11e8adeac316bf99d3ec8f45e7206b64d02 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Sat, 12 Mar 2016 14:45:15 +0200 Subject: [PATCH 2/2] ngFor test + TestApp --- tests/app/tests/renderer-tests.ts | 151 +++++++++++++----------------- tests/app/tests/test-app.ts | 75 +++++++++++++++ tests/app/tests/test-utils.ts | 29 ++++++ 3 files changed, 168 insertions(+), 87 deletions(-) create mode 100644 tests/app/tests/test-app.ts create mode 100644 tests/app/tests/test-utils.ts diff --git a/tests/app/tests/renderer-tests.ts b/tests/app/tests/renderer-tests.ts index 0825d95d6..72a14cead 100644 --- a/tests/app/tests/renderer-tests.ts +++ b/tests/app/tests/renderer-tests.ts @@ -1,38 +1,13 @@ //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, - ApplicationRef } 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, - public appRef: ApplicationRef) { - } -} +import {dumpView} from "./test-utils"; +import {TestApp} from "./test-app"; @Component({ template: `` @@ -89,72 +64,66 @@ export class StyledLabelCmp { } @Component({ - selector: "conditional-label", + selector: "ng-if-label", template: `` }) -export class ConditionalLabel { +export class NgIfLabel { public show: boolean = false; constructor(public elementRef: ElementRef) { } } +@Component({ + selector: "ng-for-label", + template: `` +}) +export class NgForLabel { + public items: Array = ["one", "two", "three"]; + constructor(public elementRef: ElementRef) { + } +} + describe('Renderer E2E', () => { - let appComponent: App = null; - let _pendingDispose: ComponentRef[] = []; - let appRef: ApplicationRef = null; - - function loadComponent(type: Type): Promise { - return appComponent.loader.loadIntoLocation(type, appComponent.elementRef, "loadSite").then((componentRef) => { - _pendingDispose.push(componentRef); - return componentRef; - }); - } + let testApp: TestApp = null; - afterEach(() => { - while (_pendingDispose.length > 0) { - const componentRef = _pendingDispose.pop() - componentRef.dispose(); - } + before(() => { + return TestApp.create().then((app) => { + testApp = app; + }) }); - 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; - appRef = appComponent.appRef; - }); + 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); @@ -163,44 +132,52 @@ describe('Renderer E2E', () => { describe("Structural directives", () => { it("ngIf hides component when false", () => { - return loadComponent(ConditionalLabel).then((componentRef) => { + return testApp.loadComponent(NgIfLabel).then((componentRef) => { const componentRoot = componentRef.instance.elementRef.nativeElement; assert.equal("(ProxyViewContainer (template))", dumpView(componentRoot)); }); }); it("ngIf show component when true", () => { - return loadComponent(ConditionalLabel).then((componentRef) => { - const component = componentRef.instance; + return testApp.loadComponent(NgIfLabel).then((componentRef) => { + const component = componentRef.instance; const componentRoot = component.elementRef.nativeElement; component.show = true; - appRef.tick(); + 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)); + }); + }); -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("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(""); +}