fix-next: navigation events' workflow in modal dialog scenarios (#5341)

This commit is contained in:
Manol Donev
2018-02-06 19:18:28 +02:00
committed by GitHub
parent b728a7384d
commit 85dc75e09a
12 changed files with 303 additions and 61 deletions

View File

@@ -141,4 +141,4 @@ else {
console.log(`TIME TO LOAD APP: ${time} ms`);
application.start({ moduleName: "app/mainPage" });
application.run({ moduleName: "app/appRoot" });

View File

@@ -0,0 +1 @@
<Frame defaultPage="app/mainPage" />

View File

@@ -1,9 +1,6 @@
import { Page } from "tns-core-modules/ui/page";
import * as trace from "tns-core-modules/trace";
import * as tests from "../testRunner";
import { Label } from "tns-core-modules/ui/label";
import * as application from "tns-core-modules/application";
import * as platform from "tns-core-modules/platform";
trace.enable();
trace.addCategories(trace.categories.Test + "," + trace.categories.Error);
@@ -18,46 +15,12 @@ trace.addCategories(trace.categories.Test + "," + trace.categories.Error);
// trace.categories.VisualTreeEvents
// ));
let page = new Page();
page.id = "mainPage";
page.on(Page.navigatedToEvent, onNavigatedTo);
function runTests() {
setTimeout(() => tests.runAll(''), 10);
}
function onNavigatedTo(args) {
let label = new Label();
label.text = "Running non-UI tests...";
page.content = label;
args.object.off(Page.navigatedToEvent, onNavigatedTo);
// Request permission to write test-results.xml file for API >= 23
// if (platform.isAndroid && parseInt(platform.device.sdkVersion) >= 23) {
// let handler = (args: application.AndroidActivityRequestPermissionsEventData) => {
// application.android.off(application.AndroidApplication.activityRequestPermissionsEvent, handler);
// if (args.requestCode === 1234 && args.grantResults.length > 0 && args.grantResults[0] === android.content.pm.PackageManager.PERMISSION_GRANTED) {
// runTests();
// } else {
// trace.write("Permission for write to external storage not granted!", trace.categories.Error, trace.messageType.error);
// }
// };
// application.android.on(application.AndroidApplication.activityRequestPermissionsEvent, handler);
// if ((<any>android.support.v4.content.ContextCompat).checkSelfPermission(application.android.currentContext, (<any>android).Manifest.permission.WRITE_EXTERNAL_STORAGE) !== android.content.pm.PackageManager.PERMISSION_GRANTED) {
// (<any>android.support.v4.app.ActivityCompat).requestPermissions(application.android.currentContext, [(<any>android).Manifest.permission.WRITE_EXTERNAL_STORAGE], 1234);
// } else {
// runTests();
// }
// } else {
// runTests();
// }
export function onNavigatedTo(args) {
args.object.off(Page.loadedEvent, onNavigatedTo);
runTests();
}
export function createPage() {
return page;
}

View File

@@ -0,0 +1,3 @@
<Page navigatedTo="onNavigatedTo">
<Label text="Running non-UI tests..." />
</Page>

View File

@@ -19,6 +19,7 @@ import * as helper from "../helper";
import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout";
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
import { View, PercentLength, Observable, unsetValue, EventData, isIOS } from "tns-core-modules/ui/core/view";
import { Frame } from "tns-core-modules/ui/frame";
import { Label } from "tns-core-modules/ui/label";
import { Color } from "tns-core-modules/color";
@@ -538,6 +539,271 @@ export function test_WhenPageIsNavigatedToItCanShowAnotherPageAsModal() {
masterPage.off(Page.navigatedToEvent, navigatedToEventHandler);
}
export function test_WhenModalPageShownHostPageNavigationEventsShouldNotBeRaised() {
let hostNavigatingToCount = 0;
let hostNavigatedToCount = 0;
let hostNavigatingFromCount = 0;
let hostNavigatedFromCount = 0;
let ready = false;
const modalCloseCallback = function (returnValue: any) {
ready = true;
}
const hostNavigatingToEventHandler = function () {
hostNavigatingToCount++;
};
const hostNavigatedToEventHandler = function () {
hostNavigatedToCount++;
};
const hostNavigatingFromEventHandler = function () {
hostNavigatingFromCount++;
};
const hostNavigatedFromEventHandler = function () {
hostNavigatedFromCount++;
};
const hostNavigatedToEventHandler2 = function(args: NavigatedData) {
const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler2);
const basePath = "ui/page/";
const entry: NavigationEntry = {
moduleName: basePath + "modal-page"
};
const modalPage = createViewFromEntry(entry) as Page;
page.showModal(modalPage, {}, modalCloseCallback, false, false);
}
const masterPageFactory = function (): Page {
const masterPage = new Page();
masterPage.id = "masterPage_test_WhenModalPageShownHostPageNavigationEventsShouldNotBeRaised";
masterPage.on(Page.navigatingToEvent, hostNavigatingToEventHandler);
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler);
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler2);
masterPage.on(Page.navigatingFromEvent, hostNavigatingFromEventHandler);
masterPage.on(Page.navigatedFromEvent, hostNavigatedFromEventHandler);
const label = new Label();
label.text = "Text";
masterPage.content = label;
return masterPage;
};
helper.navigate(masterPageFactory);
TKUnit.waitUntilReady(() => ready);
// only raised by the initial navigation to the master page
TKUnit.assertTrue(hostNavigatingToCount === 1);
TKUnit.assertTrue(hostNavigatedToCount === 1);
TKUnit.assertTrue(hostNavigatingFromCount === 0);
TKUnit.assertTrue(hostNavigatedFromCount === 0);
}
export function test_WhenModalPageShownModalNavigationToEventsShouldBeRaised() {
let modalNavigatingToCount = 0;
let modalNavigatedToCount = 0;
let modalNavigatingFromCount = 0;
let modalNavigatedFromCount = 0;
let ready = false;
const modalCloseCallback = function (returnValue: any) {
ready = true;
}
const modalNavigatingToEventHandler = function () {
modalNavigatingToCount++;
};
const modalNavigatedToEventHandler = function (args: NavigatedData) {
modalNavigatedToCount++;
(args.object as View).closeModal();
};
const modalNavigatingFromEventHandler = function () {
modalNavigatingFromCount++;
};
const modalNavigatedFromEventHandler = function () {
modalNavigatedFromCount++;
};
const modalFrameShownModallyEventHandler = function(args) {
const basePath = "ui/page/";
const entry: NavigationEntry = {
moduleName: basePath + "modal-page"
};
const modalPage = createViewFromEntry(entry) as Page;
modalPage.on(Page.navigatingToEvent, modalNavigatingToEventHandler);
modalPage.on(Page.navigatedToEvent, modalNavigatedToEventHandler);
modalPage.on(Page.navigatingFromEvent, modalNavigatingFromEventHandler);
modalPage.on(Page.navigatedFromEvent, modalNavigatedFromEventHandler);
(args.object as Frame).navigate(() => modalPage);
}
let modalFrame;
const hostNavigatedToEventHandler = function(args) {
const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
modalFrame = new Frame();
modalFrame.on(Frame.shownModallyEvent, modalFrameShownModallyEventHandler);
page.showModal(modalFrame, {}, modalCloseCallback, false, false);
}
const masterPageFactory = function (): Page {
const masterPage = new Page();
masterPage.id = "masterPage_test_WhenModalPageShownModalNavigationToEventsShouldBeRaised";
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler);
const label = new Label();
label.text = "Text";
masterPage.content = label;
return masterPage;
};
helper.navigate(masterPageFactory);
TKUnit.waitUntilReady(() => ready && !modalFrame.isLoaded);
// only raised by the initial show modal navigation
TKUnit.assertTrue(modalNavigatingToCount === 1);
TKUnit.assertTrue(modalNavigatedToCount === 1);
TKUnit.assertTrue(modalNavigatingFromCount === 0);
TKUnit.assertTrue(modalNavigatedFromCount === 0);
}
export function test_WhenModalFrameShownModalEventsRaisedOnRootModalFrame() {
let showingModallyCount = 0;
let shownModallyCount = 0;
let ready = false;
const modalCloseCallback = function (returnValue: any) {
ready = true;
}
const modalFrameShowingModallyEventHandler = function(args: ShownModallyData) {
showingModallyCount++;
}
const modalFrameShownModallyEventHandler = function(args: ShownModallyData) {
shownModallyCount++;
args.closeCallback("return value");
}
let modalFrame;
const hostNavigatedToEventHandler = function(args) {
const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
const basePath = "ui/page/";
const entry: NavigationEntry = {
moduleName: basePath + "modal-page"
};
const modalPage = createViewFromEntry(entry) as Page;
modalFrame = new Frame();
modalFrame.on(Frame.showingModallyEvent, modalFrameShowingModallyEventHandler);
modalFrame.on(Frame.shownModallyEvent, modalFrameShownModallyEventHandler);
modalFrame.navigate(() => modalPage);
page.showModal(modalFrame, {}, modalCloseCallback, false, false);
}
const masterPageFactory = function (): Page {
const masterPage = new Page();
masterPage.id = "masterPage_test_WhenModalFrameShownModalEventsRaisedOnRootModalFrame";
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler);
const label = new Label();
label.text = "Text";
masterPage.content = label;
return masterPage;
};
helper.navigate(masterPageFactory);
TKUnit.waitUntilReady(() => ready && !modalFrame.isLoaded);
TKUnit.assertTrue(showingModallyCount === 1);
TKUnit.assertTrue(shownModallyCount === 1);
}
export function test_WhenModalPageShownShowModalEventsRaisedOnRootModalPage() {
let showingModallyCount = 0;
let shownModallyCount = 0;
let ready = false;
const modalCloseCallback = function (returnValue: any) {
ready = true;
}
const modalPageShowingModallyEventHandler = function(args: ShownModallyData) {
showingModallyCount++;
}
const modalPageShownModallyEventHandler = function(args: ShownModallyData) {
shownModallyCount++;
setTimeout(() => {
args.closeCallback("return value");
}, 0);
}
const hostNavigatedToEventHandler = function(args) {
const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
const basePath = "ui/page/";
const entry: NavigationEntry = {
moduleName: basePath + "modal-page"
};
const modalPage = createViewFromEntry(entry) as Page;
modalPage.on(Page.showingModallyEvent, modalPageShowingModallyEventHandler);
modalPage.on(Page.shownModallyEvent, modalPageShownModallyEventHandler);
page.showModal(modalPage, {}, modalCloseCallback, false, false);
}
const masterPageFactory = function (): Page {
const masterPage = new Page();
masterPage.id = "masterPage_test_WhenModalPageShownShowModalEventsRaisedOnRootModalPage";
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler);
const label = new Label();
label.text = "Text";
masterPage.content = label;
return masterPage;
};
helper.navigate(masterPageFactory);
TKUnit.waitUntilReady(() => ready);
TKUnit.assertTrue(showingModallyCount === 1);
TKUnit.assertTrue(shownModallyCount === 1);
}
export function test_percent_width_and_height_support() {
const testPage = new Page();
testPage.id = "test_percent_width_and_height_support";

View File

@@ -6,10 +6,10 @@
"nativescript": {
"id": "org.nativescript.UnitTestApp",
"tns-ios": {
"version": "3.2.0"
"version": "3.4.1"
},
"tns-android": {
"version": "3.2.0"
"version": "3.4.1"
}
},
"dependencies": {

View File

@@ -40,7 +40,10 @@
"snapshot": {
"android": {
"tns-java-classes": {
"modules": ["ui/frame/activity", "ui/frame/fragment"]
"modules": [
"ui/frame/activity",
"ui/frame/fragment"
]
}
}
}

View File

@@ -85,6 +85,16 @@ export interface ShownModallyData extends EventData {
* A View occupies a rectangular area on the screen and is responsible for drawing and layouting of all UI components within.
*/
export abstract class View extends ViewBase {
/**
* String value used when hooking to showingModally event.
*/
public static showingModallyEvent: string;
/**
* String value used when hooking to shownModally event.
*/
public static shownModallyEvent: string;
/**
* Gets the android-specific native instance that lies behind this proxy. Will be available if running on an Android platform.
*/

View File

@@ -58,9 +58,18 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
@profile
public onLoaded() {
super.onLoaded();
this._pushInFrameStack();
this._processNextNavigationEntry();
}
@profile
public onUnloaded() {
super.onUnloaded();
this._popFromFrameStack();
}
public canGoBack(): boolean {
let backstack = this._backStack.length;
let previousForwardNotInBackstack = false;
@@ -183,8 +192,6 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
// app.on(app.orientationChangedEvent, (data) => this._onOrientationChanged());
// }
this._pushInFrameStack();
const backstackEntry: BackstackEntry = {
entry: entry,
resolvedPage: page,

View File

@@ -39,16 +39,6 @@ export module knownCollections {
* Represents a logical unit for navigation (inside Frame).
*/
export class Page extends ContentView {
/**
* String value used when hooking to showingModally event.
*/
public static showingModallyEvent: string;
/**
* String value used when hooking to shownModally event.
*/
public static shownModallyEvent: string;
/**
* String value used when hooking to navigatingTo event.
*/

View File

@@ -180,10 +180,11 @@ class UIViewControllerImpl extends UIViewController {
}
const frame = owner.frame;
// Skip navigation events if we are hiding because we are about to show modal page
// Skip navigation events if we are hiding because we are about to show a modal page,
// or because we are closing a modal page,
// or because we are in tab and another controller is selected.
const tab = this.tabBarController;
if (!owner._presentedViewController && frame && frame.currentPage === owner) {
if (!owner._presentedViewController && !this.presentingViewController && frame && frame.currentPage === owner) {
const willSelectViewController = tab && (<any>tab)._willSelectViewController;
if (!willSelectViewController
|| willSelectViewController === tab.selectedViewController) {

View File

@@ -1,13 +1,11 @@
{
"name": "tns-platform-declarations",
"version": "4.0.0",
"description":
"Platform-specific TypeScript declarations for NativeScript for accessing native objects",
"description": "Platform-specific TypeScript declarations for NativeScript for accessing native objects",
"main": "",
"scripts": {
"test": "tsc",
"package-tag":
"npm version $npm_package_version-$PACKAGE_VERSION --no-git-tag-version"
"package-tag": "npm version $npm_package_version-$PACKAGE_VERSION --no-git-tag-version"
},
"repository": {
"type": "git",