Skip to content

Commit 4ce4566

Browse files
authored
test: add reset root view tests (NativeScript#5437)
1 parent c7dd02c commit 4ce4566

File tree

9 files changed

+198
-19
lines changed

9 files changed

+198
-19
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import * as TKUnit from "../TKUnit";
2+
import { Page } from "tns-core-modules/ui/page";
3+
import { Frame, NavigationEntry, stack } from "tns-core-modules/ui/frame";
4+
import { _resetRootView, getRootView } from "tns-core-modules/application";
5+
import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view";
6+
7+
function createTestFrameRootEntry() {
8+
const page = new Page();
9+
const frameRoot = new Frame();
10+
frameRoot.navigate(() => page);
11+
12+
const entry: NavigationEntry = {
13+
create: () => frameRoot
14+
};
15+
16+
return {
17+
entry: entry,
18+
root: frameRoot,
19+
page: page
20+
};
21+
}
22+
23+
function createTestTabRootEntry() {
24+
const testFrameRoot1 = createTestFrameRootEntry();
25+
const testFrameRoot2 = createTestFrameRootEntry();
26+
27+
const tabView = new TabView();
28+
const tabEntry1 = new TabViewItem();
29+
tabEntry1.title = "frameRoot1";
30+
tabEntry1.view = testFrameRoot1.root;
31+
const tabEntry2 = new TabViewItem();
32+
tabEntry2.title = "frameRoot2";
33+
tabEntry2.view = testFrameRoot2.root;
34+
tabView.items = [tabEntry1, tabEntry2];
35+
36+
const entry: NavigationEntry = {
37+
create: () => tabView
38+
};
39+
40+
return {
41+
entry: entry,
42+
root: tabView,
43+
page: testFrameRoot1.page
44+
};
45+
}
46+
47+
export function test_reset_frame_to_frame() {
48+
const testFrameRoot1 = createTestFrameRootEntry();
49+
50+
_resetRootView(testFrameRoot1.entry);
51+
TKUnit.waitUntilReady(() => testFrameRoot1.page.isLoaded);
52+
53+
const rootView1 = getRootView();
54+
const frameStack1 = stack();
55+
TKUnit.assertEqual(rootView1, testFrameRoot1.root);
56+
TKUnit.assertEqual(frameStack1.length, 1);
57+
58+
const testFrameRoot2 = createTestFrameRootEntry();
59+
60+
_resetRootView(testFrameRoot2.entry);
61+
TKUnit.waitUntilReady(() => testFrameRoot2.page.isLoaded);
62+
63+
const rootView2 = getRootView();
64+
const frameStack2 = stack();
65+
TKUnit.assertEqual(rootView2, testFrameRoot2.root);
66+
TKUnit.assertEqual(frameStack2.length, 1);
67+
};
68+
69+
export function test_reset_frame_to_tab() {
70+
const testFrameRoot = createTestFrameRootEntry();
71+
72+
_resetRootView(testFrameRoot.entry);
73+
TKUnit.waitUntilReady(() => testFrameRoot.page.isLoaded);
74+
75+
const rootView1 = getRootView();
76+
const frameStack1 = stack();
77+
TKUnit.assertEqual(rootView1, testFrameRoot.root);
78+
TKUnit.assertEqual(frameStack1.length, 1);
79+
80+
const testTabRoot = createTestTabRootEntry();
81+
82+
_resetRootView(testTabRoot.entry);
83+
TKUnit.waitUntilReady(() => testTabRoot.page.isLoaded);
84+
85+
const rootView2 = getRootView();
86+
const frameStack2 = stack();
87+
TKUnit.assertEqual(rootView2, testTabRoot.root);
88+
TKUnit.assertEqual(frameStack2.length, 2);
89+
};
90+
91+
export function test_reset_tab_to_frame() {
92+
const testTabRoot = createTestTabRootEntry();
93+
94+
_resetRootView(testTabRoot.entry);
95+
TKUnit.waitUntilReady(() => testTabRoot.page.isLoaded);
96+
97+
const rootView2 = getRootView();
98+
const frameStack2 = stack();
99+
TKUnit.assertEqual(rootView2, testTabRoot.root);
100+
TKUnit.assertEqual(frameStack2.length, 2);
101+
102+
const testFrameRoot = createTestFrameRootEntry();
103+
104+
_resetRootView(testFrameRoot.entry);
105+
TKUnit.waitUntilReady(() => testFrameRoot.page.isLoaded);
106+
107+
const rootView1 = getRootView();
108+
const frameStack1 = stack();
109+
TKUnit.assertEqual(rootView1, testFrameRoot.root);
110+
TKUnit.assertEqual(frameStack1.length, 1);
111+
};
112+
113+
export function test_reset_tab_to_tab() {
114+
const testTabRoot1 = createTestTabRootEntry();
115+
116+
_resetRootView(testTabRoot1.entry);
117+
TKUnit.waitUntilReady(() => testTabRoot1.page.isLoaded);
118+
119+
const rootView1 = getRootView();
120+
const frameStack1 = stack();
121+
TKUnit.assertEqual(rootView1, testTabRoot1.root);
122+
TKUnit.assertEqual(frameStack1.length, 2);
123+
124+
const testTabRoot2 = createTestTabRootEntry();
125+
126+
_resetRootView(testTabRoot2.entry);
127+
TKUnit.waitUntilReady(() => testTabRoot2.page.isLoaded);
128+
129+
const rootView2 = getRootView();
130+
const frameStack2 = stack();
131+
TKUnit.assertEqual(rootView2, testTabRoot2.root);
132+
TKUnit.assertEqual(frameStack2.length, 2);
133+
};
134+
135+
export function tearDownModule() {
136+
// reset the root to frame for other tests
137+
const resetFrameRoot = createTestFrameRootEntry();
138+
139+
_resetRootView(resetFrameRoot.entry);
140+
TKUnit.waitUntilReady(() => resetFrameRoot.page.isLoaded);
141+
}

tests/app/testRunner.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/* tslint:disable */
22
import * as TKUnit from "./TKUnit";
3+
import { _resetRootView, getRootView } from "tns-core-modules/application";
34
import { messageType } from "tns-core-modules/trace";
4-
import { topmost, Frame } from "tns-core-modules/ui/frame";
5+
import { topmost, Frame, NavigationEntry } from "tns-core-modules/ui/frame";
56
import { Page } from "tns-core-modules/ui/page";
67
import { TextView } from "tns-core-modules/ui/text-view";
78
import { Button } from "tns-core-modules/ui/button";
@@ -233,6 +234,9 @@ allTests["SEARCH-BAR"] = searchBarTests;
233234
import * as navigationTests from "./navigation/navigation-tests";
234235
allTests["NAVIGATION"] = navigationTests;
235236

237+
import * as resetRootViewTests from "./navigation/reset-root-view-tests";
238+
allTests["RESET-ROOT-VIEW"] = resetRootViewTests;
239+
236240
const testsSuitesWithLongDelay = {
237241
HTTP: 15 * 1000,
238242
}

tns-core-modules/application/application-common.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
require("globals");
33

44
import { Observable, EventData } from "../data/observable";
5-
// types
6-
import { View } from "../ui/core/view";
75
import {
86
trace as profilingTrace,
97
time,

tns-core-modules/application/application.android.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ setApplication(androidApp);
126126
let mainEntry: NavigationEntry;
127127
let started = false;
128128
// NOTE: for backwards compatibility. Remove for 4.0.0.
129-
let createRootFrame = true;
129+
const createRootFrame = { value: true };
130130
export function start(entry?: NavigationEntry | string) {
131131
if (started) {
132132
throw new Error("Application is already started.");
@@ -141,11 +141,11 @@ export function start(entry?: NavigationEntry | string) {
141141
}
142142

143143
export function shouldCreateRootFrame(): boolean {
144-
return createRootFrame;
144+
return createRootFrame.value;
145145
}
146146

147147
export function run(entry?: NavigationEntry | string) {
148-
createRootFrame = false;
148+
createRootFrame.value = false;
149149
start(entry);
150150
}
151151

@@ -157,6 +157,7 @@ export function _resetRootView(entry?: NavigationEntry | string) {
157157
throw new Error("Cannot find android activity.");
158158
}
159159

160+
createRootFrame.value = false;
160161
mainEntry = typeof entry === "string" ? { moduleName: entry } : entry;
161162
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
162163
callbacks.resetActivityContent(activity);
@@ -166,7 +167,7 @@ export function getMainEntry() {
166167
return mainEntry;
167168
}
168169

169-
export function getRootView() {
170+
export function getRootView(): View {
170171
// Use start activity as a backup when foregroundActivity is still not set
171172
// in cases when we are getting the root view before activity.onResumed event is fired
172173
const activity = androidApp.foregroundActivity || androidApp.startActivity;

tns-core-modules/application/application.ios.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class IOSApplication implements IOSApplicationDefinition {
100100
}
101101
}
102102

103-
get rootView() : View {
103+
get rootView(): View {
104104
return this._rootView;
105105
}
106106

@@ -213,11 +213,16 @@ class IOSApplication implements IOSApplicationDefinition {
213213
}
214214

215215
public setWindowContent(view?: View): void {
216+
if (this._rootView) {
217+
// if we already have a root view, we reset it.
218+
this._rootView._onRootViewReset();
219+
}
220+
216221
const rootView = createRootView(view);
217222
this._rootView = rootView;
218223
const controller = getViewController(rootView);
219224

220-
if (createRootFrame) {
225+
if (createRootFrame.value) {
221226
// Don't setup as styleScopeHost
222227
rootView._setupUI({});
223228
} else {
@@ -248,7 +253,7 @@ function createRootView(v?: View) {
248253
if (!rootView) {
249254
// try to navigate to the mainEntry (if specified)
250255
if (mainEntry) {
251-
if (createRootFrame) {
256+
if (createRootFrame.value) {
252257
const frame = rootView = new Frame();
253258
frame.navigate(mainEntry);
254259
} else {
@@ -272,7 +277,7 @@ export function getRootView() {
272277
}
273278

274279
// NOTE: for backwards compatibility. Remove for 4.0.0.
275-
let createRootFrame = true;
280+
const createRootFrame = { value: true };
276281
let started: boolean = false;
277282
export function start(entry?: string | NavigationEntry) {
278283
mainEntry = typeof entry === "string" ? { moduleName: entry } : entry;
@@ -300,12 +305,12 @@ export function start(entry?: string | NavigationEntry) {
300305
}
301306

302307
export function run(entry?: string | NavigationEntry) {
303-
createRootFrame = false;
308+
createRootFrame.value = false;
304309
start(entry);
305310
}
306311

307312
export function _resetRootView(entry?: NavigationEntry | string) {
308-
createRootFrame = false;
313+
createRootFrame.value = false;
309314
mainEntry = typeof entry === "string" ? { moduleName: entry } : entry;
310315
iosApp.setWindowContent();
311316
}

tns-core-modules/ui/core/view-base/view-base.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ export abstract class ViewBase extends Observable {
267267
* Method is intended to be overridden by inheritors and used as "protected"
268268
*/
269269
public _dialogClosed(): void;
270+
/**
271+
* Method is intended to be overridden by inheritors and used as "protected"
272+
*/
273+
public _onRootViewReset(): void;
270274

271275
_domId: number;
272276

tns-core-modules/ui/core/view-base/view-base.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,13 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
961961
public _dialogClosed(): void {
962962
return;
963963
}
964+
965+
public _onRootViewReset(): void {
966+
eachDescendant(this, (child: ViewBase) => {
967+
child._onRootViewReset();
968+
return true;
969+
});
970+
}
964971
}
965972

966973
ViewBase.prototype.isCollapsed = false;

tns-core-modules/ui/frame/frame-common.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
5858
@profile
5959
public onLoaded() {
6060
super.onLoaded();
61-
61+
6262
this._processNextNavigationEntry();
6363
}
6464

@@ -424,10 +424,25 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
424424
this._isInFrameStack = false;
425425
}
426426

427+
private _removeFromFrameStack() {
428+
if (!this._isInFrameStack) {
429+
return;
430+
}
431+
432+
const index = frameStack.indexOf(this);
433+
frameStack.splice(index, 1);
434+
this._isInFrameStack = false;
435+
}
436+
427437
public _dialogClosed(): void {
428438
this._popFromFrameStack();
429439
}
430440

441+
public _onRootViewReset(): void {
442+
this._removeFromFrameStack();
443+
super._onRootViewReset();
444+
}
445+
431446
get _childrenCount(): number {
432447
if (this.currentPage) {
433448
return 1;

tns-core-modules/ui/frame/frame.android.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ export class Frame extends FrameBase {
244244
this.goBack();
245245
return true;
246246
}
247-
247+
248248
if (!this.navigationQueueIsEmpty()) {
249249
const manager = this._getFragmentManager();
250250
if (manager) {
@@ -935,6 +935,10 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
935935
}
936936

937937
public resetActivityContent(activity: android.app.Activity): void {
938+
if (this._rootView) {
939+
// if we already have a root view, we reset it.
940+
this._rootView._onRootViewReset();
941+
}
938942
// Delete previously cached root view in order to recreate it.
939943
this._rootView = null;
940944
this.setActivityContent(activity, null, false);
@@ -1020,11 +1024,11 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
10201024

10211025
const notifyLaunch = profile("notifyLaunch", function notifyLaunch(intent: android.content.Intent, savedInstanceState: android.os.Bundle): View {
10221026
const launchArgs: application.LaunchEventData = {
1023-
eventName: application.launchEvent,
1024-
object: application.android,
1025-
android: intent, savedInstanceState
1027+
eventName: application.launchEvent,
1028+
object: application.android,
1029+
android: intent, savedInstanceState
10261030
};
1027-
1031+
10281032
application.notify(launchArgs);
10291033
application.notify(<application.LoadAppCSSEventData>{ eventName: "loadAppCss", object: <any>this, cssFile: application.getCssFileName() });
10301034
return launchArgs.root;

0 commit comments

Comments
 (0)