Skip to content

Structural directives tests + TestApp #109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 93 additions & 81 deletions tests/app/tests/renderer-tests.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,27 @@
//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: `<StackLayout #loadSite></StackLayout>`
})
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: `<StackLayout><Label text="Layout"></Label></StackLayout>`
})
export class LayoutWithLabel {
constructor(public elementRef: ElementRef){}
constructor(public elementRef: ElementRef) { }
}

@Component({
selector: "label-cmp",
template: `<Label text="Layout"></Label>`
})
export class LabelCmp {
constructor(public elementRef: ElementRef){
constructor(public elementRef: ElementRef) {
}
}

Expand All @@ -53,15 +30,15 @@ export class LabelCmp {
template: `<GridLayout><label-cmp></label-cmp></GridLayout>`
})
export class LabelContainer {
constructor(public elementRef: ElementRef){}
constructor(public elementRef: ElementRef) { }
}

@Component({
selector: "projectable-cmp",
template: `<StackLayout><ng-content></ng-content></StackLayout>`
})
export class ProjectableCmp {
constructor(public elementRef: ElementRef){
constructor(public elementRef: ElementRef) {
}
}
@Component({
Expand All @@ -71,7 +48,7 @@ export class ProjectableCmp {
</GridLayout>`
})
export class ProjectionContainer {
constructor(public elementRef: ElementRef){}
constructor(public elementRef: ElementRef) { }
}

@Component({
Expand All @@ -82,90 +59,125 @@ export class ProjectionContainer {
template: `<Label text="Styled!"></Label>`
})
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: `<Label *ngIf="show" text="iffed"></Label>`
})
export class NgIfLabel {
public show: boolean = false;
constructor(public elementRef: ElementRef) {
}
}

function loadComponent(type: Type): Promise<ComponentRef> {
return appComponent.loader.loadIntoLocation(type, appComponent.elementRef, "loadSite").then((componentRef) => {
_pendingDispose.push(componentRef);
return componentRef;
});
@Component({
selector: "ng-for-label",
template: `<Label *ngFor="#item of items" [text]="item"></Label>`
})
export class NgForLabel {
public items: Array<string> = ["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 = <LayoutBase>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 = (<ProxyViewContainer>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 = (<any>view).nodeName
if (!nodeName) {
nodeName = (<any>view.constructor).name + '!';
}
let output = ["(", nodeName, " "];
(<any>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 = <NgIfLabel>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 = <NgForLabel>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 = <NgForLabel>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));
});
});
})
})
75 changes: 75 additions & 0 deletions tests/app/tests/test-app.ts
Original file line number Diff line number Diff line change
@@ -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: `<StackLayout #loadSite></StackLayout>`
})
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<ComponentRef> {
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<TestApp> {
const page = topmost().currentPage;
const rootLayout = <LayoutBase>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 = <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;
}
}
29 changes: 29 additions & 0 deletions tests/app/tests/test-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {View} from "ui/core/view";
import {TextBase} from "ui/text-base";

function getChildren(view: View): Array<View> {
var children: Array<View> = [];
(<any>view)._eachChildView((child) => {
children.push(child);
return true;
});
return children;
}

export function dumpView(view: View, verbose: boolean = false): string {
let nodeName = (<any>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("");
}