diff --git a/tests/app/app/app.ts b/tests/app/app/app.ts index d898ff4e6..6b35dfacf 100644 --- a/tests/app/app/app.ts +++ b/tests/app/app/app.ts @@ -141,4 +141,4 @@ else { console.log(`TIME TO LOAD APP: ${time} ms`); -application.start({ moduleName: "app/mainPage" }); +application.run({ moduleName: "app/appRoot" }); diff --git a/tests/app/app/appRoot.xml b/tests/app/app/appRoot.xml new file mode 100644 index 000000000..b78a0e763 --- /dev/null +++ b/tests/app/app/appRoot.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/app/app/mainPage.ts b/tests/app/app/mainPage.ts index 049b79ac1..786b9320c 100644 --- a/tests/app/app/mainPage.ts +++ b/tests/app/app/mainPage.ts @@ -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 ((android.support.v4.content.ContextCompat).checkSelfPermission(application.android.currentContext, (android).Manifest.permission.WRITE_EXTERNAL_STORAGE) !== android.content.pm.PackageManager.PERMISSION_GRANTED) { - // (android.support.v4.app.ActivityCompat).requestPermissions(application.android.currentContext, [(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; -} \ No newline at end of file diff --git a/tests/app/app/mainPage.xml b/tests/app/app/mainPage.xml new file mode 100644 index 000000000..bee9a71d8 --- /dev/null +++ b/tests/app/app/mainPage.xml @@ -0,0 +1,3 @@ + + diff --git a/tests/app/ui/page/page-tests-common.ts b/tests/app/ui/page/page-tests-common.ts index 0a31f8bdd..4b8ee7569 100644 --- a/tests/app/ui/page/page-tests-common.ts +++ b/tests/app/ui/page/page-tests-common.ts @@ -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 = 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 = 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 = 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 = 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"; diff --git a/tests/package.json b/tests/package.json index dad1fda17..29a382188 100644 --- a/tests/package.json +++ b/tests/package.json @@ -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": { diff --git a/tns-core-modules/package.json b/tns-core-modules/package.json index 9675da85b..1aa48fdae 100644 --- a/tns-core-modules/package.json +++ b/tns-core-modules/package.json @@ -40,7 +40,10 @@ "snapshot": { "android": { "tns-java-classes": { - "modules": ["ui/frame/activity", "ui/frame/fragment"] + "modules": [ + "ui/frame/activity", + "ui/frame/fragment" + ] } } } diff --git a/tns-core-modules/ui/core/view/view.d.ts b/tns-core-modules/ui/core/view/view.d.ts index e2740068b..7ab7f6209 100644 --- a/tns-core-modules/ui/core/view/view.d.ts +++ b/tns-core-modules/ui/core/view/view.d.ts @@ -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. */ diff --git a/tns-core-modules/ui/frame/frame-common.ts b/tns-core-modules/ui/frame/frame-common.ts index f111b08b5..3e43cfb17 100644 --- a/tns-core-modules/ui/frame/frame-common.ts +++ b/tns-core-modules/ui/frame/frame-common.ts @@ -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, diff --git a/tns-core-modules/ui/page/page.d.ts b/tns-core-modules/ui/page/page.d.ts index a75c13268..a9ace3677 100644 --- a/tns-core-modules/ui/page/page.d.ts +++ b/tns-core-modules/ui/page/page.d.ts @@ -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. */ diff --git a/tns-core-modules/ui/page/page.ios.ts b/tns-core-modules/ui/page/page.ios.ts index 00aa75415..bd3e42c25 100644 --- a/tns-core-modules/ui/page/page.ios.ts +++ b/tns-core-modules/ui/page/page.ios.ts @@ -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 && (tab)._willSelectViewController; if (!willSelectViewController || willSelectViewController === tab.selectedViewController) { diff --git a/tns-platform-declarations/package.json b/tns-platform-declarations/package.json index 7fe6b1b98..3e0800e08 100644 --- a/tns-platform-declarations/package.json +++ b/tns-platform-declarations/package.json @@ -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",