Skip to content

Commit 42bbb11

Browse files
committed
ngFor test + TestApp
1 parent 1c27b1b commit 42bbb11

File tree

3 files changed

+168
-87
lines changed

3 files changed

+168
-87
lines changed

tests/app/tests/renderer-tests.ts

Lines changed: 64 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,13 @@
11
//make sure you import mocha-config before angular2/core
22
import {assert} from "./test-config";
3-
import {bootstrap} from "../nativescript-angular/application";
43
import {
5-
Type,
64
Component,
7-
ComponentRef,
8-
DynamicComponentLoader,
9-
ViewChild,
105
ElementRef,
11-
provide,
12-
ApplicationRef
136
} from "angular2/core";
14-
import {View} from "ui/core/view";
15-
import * as background from "ui/styling/background";
16-
import {StackLayout} from "ui/layouts/stack-layout";
17-
import {GridLayout} from "ui/layouts/grid-layout";
18-
import {LayoutBase} from "ui/layouts/layout-base";
197
import {ProxyViewContainer} from "ui/proxy-view-container";
20-
import {topmost} from 'ui/frame';
21-
import {APP_ROOT_VIEW} from "../nativescript-angular/platform-providers";
228
import {Red} from "color/known-colors";
23-
24-
@Component({
25-
selector: 'my-app',
26-
template: `<StackLayout #loadSite></StackLayout>`
27-
})
28-
export class App {
29-
@ViewChild("loadSite") public loadSiteRef: ElementRef;
30-
31-
constructor(public loader: DynamicComponentLoader,
32-
public elementRef: ElementRef,
33-
public appRef: ApplicationRef) {
34-
}
35-
}
9+
import {dumpView} from "./test-utils";
10+
import {TestApp} from "./test-app";
3611

3712
@Component({
3813
template: `<StackLayout><Label text="Layout"></Label></StackLayout>`
@@ -89,72 +64,66 @@ export class StyledLabelCmp {
8964
}
9065

9166
@Component({
92-
selector: "conditional-label",
67+
selector: "ng-if-label",
9368
template: `<Label *ngIf="show" text="iffed"></Label>`
9469
})
95-
export class ConditionalLabel {
70+
export class NgIfLabel {
9671
public show: boolean = false;
9772
constructor(public elementRef: ElementRef) {
9873
}
9974
}
10075

76+
@Component({
77+
selector: "ng-for-label",
78+
template: `<Label *ngFor="#item of items" [text]="item"></Label>`
79+
})
80+
export class NgForLabel {
81+
public items: Array<string> = ["one", "two", "three"];
82+
constructor(public elementRef: ElementRef) {
83+
}
84+
}
85+
10186

10287
describe('Renderer E2E', () => {
103-
let appComponent: App = null;
104-
let _pendingDispose: ComponentRef[] = [];
105-
let appRef: ApplicationRef = null;
106-
107-
function loadComponent(type: Type): Promise<ComponentRef> {
108-
return appComponent.loader.loadIntoLocation(type, appComponent.elementRef, "loadSite").then((componentRef) => {
109-
_pendingDispose.push(componentRef);
110-
return componentRef;
111-
});
112-
}
88+
let testApp: TestApp = null;
11389

114-
afterEach(() => {
115-
while (_pendingDispose.length > 0) {
116-
const componentRef = _pendingDispose.pop()
117-
componentRef.dispose();
118-
}
90+
before(() => {
91+
return TestApp.create().then((app) => {
92+
testApp = app;
93+
})
11994
});
12095

121-
before(() => {
122-
//bootstrap the app in a custom location
123-
const page = topmost().currentPage;
124-
const rootLayout = <LayoutBase>page.content;
125-
const viewRoot = new StackLayout();
126-
rootLayout.addChild(viewRoot);
127-
GridLayout.setRow(rootLayout, 50);
128-
const rootViewProvider = provide(APP_ROOT_VIEW, { useFactory: () => viewRoot });
129-
return bootstrap(App, [rootViewProvider]).then((componentRef) => {
130-
appComponent = componentRef.instance;
131-
appRef = appComponent.appRef;
132-
});
96+
after(() => {
97+
testApp.dispose();
98+
});
99+
100+
afterEach(() => {
101+
testApp.disposeComponenets();
133102
});
134103

135104
it("component with a layout", () => {
136-
return loadComponent(LayoutWithLabel).then((componentRef) => {
105+
return testApp.loadComponent(LayoutWithLabel).then((componentRef) => {
137106
const componentRoot = componentRef.instance.elementRef.nativeElement;
138107
assert.equal("(ProxyViewContainer (StackLayout (Label)))", dumpView(componentRoot));
139108
});
140109
});
141110

142111
it("component without a layout", () => {
143-
return loadComponent(LabelContainer).then((componentRef) => {
112+
return testApp.loadComponent(LabelContainer).then((componentRef) => {
144113
const componentRoot = componentRef.instance.elementRef.nativeElement;
145114
assert.equal("(ProxyViewContainer (GridLayout (ProxyViewContainer (Label))))", dumpView(componentRoot));
146115
});
147116
});
148117

149118
it("projects content into components", () => {
150-
return loadComponent(ProjectionContainer).then((componentRef) => {
119+
return testApp.loadComponent(ProjectionContainer).then((componentRef) => {
151120
const componentRoot = componentRef.instance.elementRef.nativeElement;
152121
assert.equal("(ProxyViewContainer (GridLayout (ProxyViewContainer (StackLayout (Button)))))", dumpView(componentRoot));
153122
});
154123
});
155124

156125
it("applies component styles", () => {
157-
return loadComponent(StyledLabelCmp).then((componentRef) => {
126+
return testApp.loadComponent(StyledLabelCmp).then((componentRef) => {
158127
const componentRoot = componentRef.instance.elementRef.nativeElement;
159128
const label = (<ProxyViewContainer>componentRoot).getChildAt(0);
160129
assert.equal(Red, label.style.color.hex);
@@ -163,44 +132,52 @@ describe('Renderer E2E', () => {
163132

164133
describe("Structural directives", () => {
165134
it("ngIf hides component when false", () => {
166-
return loadComponent(ConditionalLabel).then((componentRef) => {
135+
return testApp.loadComponent(NgIfLabel).then((componentRef) => {
167136
const componentRoot = componentRef.instance.elementRef.nativeElement;
168137
assert.equal("(ProxyViewContainer (template))", dumpView(componentRoot));
169138
});
170139
});
171140

172141
it("ngIf show component when true", () => {
173-
return loadComponent(ConditionalLabel).then((componentRef) => {
174-
const component = <ConditionalLabel>componentRef.instance;
142+
return testApp.loadComponent(NgIfLabel).then((componentRef) => {
143+
const component = <NgIfLabel>componentRef.instance;
175144
const componentRoot = component.elementRef.nativeElement;
176145

177146
component.show = true;
178-
appRef.tick();
147+
testApp.appRef.tick();
179148
assert.equal("(ProxyViewContainer (template), (Label))", dumpView(componentRoot));
180149
});
181150
})
182-
})
183151

184-
});
152+
it("ngFor creates element for each item", () => {
153+
return testApp.loadComponent(NgForLabel).then((componentRef) => {
154+
const componentRoot = componentRef.instance.elementRef.nativeElement;
155+
assert.equal("(ProxyViewContainer (template), (Label[text=one]), (Label[text=two]), (Label[text=three]))", dumpView(componentRoot, true));
156+
});
157+
});
185158

186-
function dumpView(view: View): string {
187-
let nodeName = (<any>view).nodeName
188-
if (!nodeName) {
189-
nodeName = (<any>view.constructor).name + '!';
190-
}
191-
let output = ["(", nodeName, " "];
192-
(<any>view)._eachChildView((child) => {
193-
const childDump = dumpView(child);
194-
output.push(childDump);
195-
output.push(", ");
196-
return true;
197-
});
198-
if (output[output.length - 1] == ", ") {
199-
output.pop();
200-
}
201-
if (output[output.length - 1] == " ") {
202-
output.pop();
203-
}
204-
output.push(")");
205-
return output.join("");
206-
}
159+
it("ngFor updates when item is removed", () => {
160+
return testApp.loadComponent(NgForLabel).then((componentRef) => {
161+
const component = <NgForLabel>componentRef.instance;
162+
const componentRoot = component.elementRef.nativeElement;
163+
164+
component.items.splice(1, 1);
165+
testApp.appRef.tick();
166+
167+
assert.equal("(ProxyViewContainer (template), (Label[text=one]), (Label[text=three]))", dumpView(componentRoot, true));
168+
});
169+
});
170+
171+
it("ngFor updates when item is inserted", () => {
172+
return testApp.loadComponent(NgForLabel).then((componentRef) => {
173+
const component = <NgForLabel>componentRef.instance;
174+
const componentRoot = component.elementRef.nativeElement;
175+
176+
component.items.splice(1, 0, "new");
177+
testApp.appRef.tick();
178+
179+
assert.equal("(ProxyViewContainer (template), (Label[text=one]), (Label[text=new]), (Label[text=two]), (Label[text=three]))", dumpView(componentRoot, true));
180+
});
181+
});
182+
})
183+
})

tests/app/tests/test-app.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//make sure you import mocha-config before angular2/core
2+
import {bootstrap} from "../nativescript-angular/application";
3+
import {
4+
Type,
5+
Component,
6+
ComponentRef,
7+
DynamicComponentLoader,
8+
ViewChild,
9+
ElementRef,
10+
provide,
11+
ApplicationRef
12+
} from "angular2/core";
13+
14+
import {View} from "ui/core/view";
15+
import {StackLayout} from "ui/layouts/stack-layout";
16+
import {GridLayout} from "ui/layouts/grid-layout";
17+
import {LayoutBase} from "ui/layouts/layout-base";
18+
import {topmost} from 'ui/frame';
19+
import {APP_ROOT_VIEW} from "../nativescript-angular/platform-providers";
20+
21+
@Component({
22+
selector: 'my-app',
23+
template: `<StackLayout #loadSite></StackLayout>`
24+
})
25+
export class TestApp {
26+
@ViewChild("loadSite") public loadSiteRef: ElementRef;
27+
private _pageRoot: LayoutBase;
28+
private _appRoot: StackLayout;
29+
private _pendingDispose: ComponentRef[] = [];
30+
31+
constructor(public loader: DynamicComponentLoader,
32+
public elementRef: ElementRef,
33+
public appRef: ApplicationRef) {
34+
}
35+
36+
public loadComponent(type: Type): Promise<ComponentRef> {
37+
return this.loader.loadIntoLocation(type, this.elementRef, "loadSite").then((componentRef) => {
38+
this._pendingDispose.push(componentRef);
39+
this.appRef.tick();
40+
return componentRef;
41+
});
42+
}
43+
44+
public disposeComponenets() {
45+
while (this._pendingDispose.length > 0) {
46+
const componentRef = this._pendingDispose.pop()
47+
componentRef.dispose();
48+
}
49+
}
50+
51+
public static create(): Promise<TestApp> {
52+
const page = topmost().currentPage;
53+
const rootLayout = <LayoutBase>page.content;
54+
const viewRoot = new StackLayout();
55+
rootLayout.addChild(viewRoot);
56+
GridLayout.setRow(rootLayout, 50);
57+
const rootViewProvider = provide(APP_ROOT_VIEW, { useFactory: () => viewRoot });
58+
return bootstrap(TestApp, [rootViewProvider]).then((componentRef) => {
59+
const testApp = <TestApp>componentRef.instance;
60+
testApp._pageRoot = rootLayout;
61+
testApp._appRoot = viewRoot;
62+
return testApp;
63+
});
64+
}
65+
66+
public dispose() {
67+
if (!this._appRoot) {
68+
throw new Error("Test app already disposed or not initalized.");
69+
}
70+
this.disposeComponenets();
71+
this._pageRoot.removeChild(this._appRoot);
72+
this._appRoot = null;
73+
this._pageRoot = null;
74+
}
75+
}

tests/app/tests/test-utils.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {View} from "ui/core/view";
2+
import {TextBase} from "ui/text-base";
3+
4+
function getChildren(view: View): Array<View> {
5+
var children: Array<View> = [];
6+
(<any>view)._eachChildView((child) => {
7+
children.push(child);
8+
return true;
9+
});
10+
return children;
11+
}
12+
13+
export function dumpView(view: View, verbose: boolean = false): string {
14+
let nodeName = (<any>view).nodeName
15+
let output = ["(", nodeName];
16+
if (verbose) {
17+
if (view instanceof TextBase) {
18+
output.push("[text=", view.text, "]")
19+
}
20+
}
21+
22+
let children = getChildren(view).map((c) => dumpView(c, verbose)).join(", ");
23+
if (children) {
24+
output.push(" ", children);
25+
}
26+
27+
output.push(")");
28+
return output.join("");
29+
}

0 commit comments

Comments
 (0)