From 27622d83ba823f1d57c14a8c178fcbf2fff212d5 Mon Sep 17 00:00:00 2001 From: Martin Yankov Date: Wed, 28 Feb 2018 10:40:05 +0200 Subject: [PATCH] fix-next: ensure proper events on tab change (#5468) --- tests/app/navigation/reset-root-view-tests.ts | 39 ++++---- tests/app/testRunner.ts | 3 + tests/app/ui/helper.ts | 12 +++ tests/app/ui/tab-view/tab-view-root-tests.ts | 91 +++++++++++++++++++ tns-core-modules/ui/frame/frame.android.ts | 6 +- tns-core-modules/ui/frame/frame.ios.ts | 8 +- tns-core-modules/ui/tab-view/tab-view.ios.ts | 7 +- 7 files changed, 141 insertions(+), 25 deletions(-) create mode 100644 tests/app/ui/tab-view/tab-view-root-tests.ts diff --git a/tests/app/navigation/reset-root-view-tests.ts b/tests/app/navigation/reset-root-view-tests.ts index dd8256f0a..8d566eea3 100644 --- a/tests/app/navigation/reset-root-view-tests.ts +++ b/tests/app/navigation/reset-root-view-tests.ts @@ -1,4 +1,5 @@ import * as TKUnit from "../TKUnit"; +import * as helper from "../ui/helper"; import { Page } from "tns-core-modules/ui/page"; import { Frame, NavigationEntry, stack } from "tns-core-modules/ui/frame"; import { _resetRootView, getRootView } from "tns-core-modules/application"; @@ -47,8 +48,7 @@ function createTestTabRootEntry() { export function test_reset_frame_to_frame() { const testFrameRoot1 = createTestFrameRootEntry(); - _resetRootView(testFrameRoot1.entry); - TKUnit.waitUntilReady(() => testFrameRoot1.page.isLoaded); + helper.waitUntilNavigatedTo(testFrameRoot1.page, () => _resetRootView(testFrameRoot1.entry)); const rootView1 = getRootView(); const frameStack1 = stack(); @@ -57,8 +57,7 @@ export function test_reset_frame_to_frame() { const testFrameRoot2 = createTestFrameRootEntry(); - _resetRootView(testFrameRoot2.entry); - TKUnit.waitUntilReady(() => testFrameRoot2.page.isLoaded); + helper.waitUntilNavigatedTo(testFrameRoot2.page, () => _resetRootView(testFrameRoot2.entry)); const rootView2 = getRootView(); const frameStack2 = stack(); @@ -69,8 +68,7 @@ export function test_reset_frame_to_frame() { export function test_reset_frame_to_tab() { const testFrameRoot = createTestFrameRootEntry(); - _resetRootView(testFrameRoot.entry); - TKUnit.waitUntilReady(() => testFrameRoot.page.isLoaded); + helper.waitUntilNavigatedTo(testFrameRoot.page, () => _resetRootView(testFrameRoot.entry)); const rootView1 = getRootView(); const frameStack1 = stack(); @@ -79,8 +77,7 @@ export function test_reset_frame_to_tab() { const testTabRoot = createTestTabRootEntry(); - _resetRootView(testTabRoot.entry); - TKUnit.waitUntilReady(() => testTabRoot.page.isLoaded); + helper.waitUntilNavigatedTo(testTabRoot.page, () => _resetRootView(testTabRoot.entry)); const rootView2 = getRootView(); const frameStack2 = stack(); @@ -91,8 +88,7 @@ export function test_reset_frame_to_tab() { export function test_reset_tab_to_frame() { const testTabRoot = createTestTabRootEntry(); - _resetRootView(testTabRoot.entry); - TKUnit.waitUntilReady(() => testTabRoot.page.isLoaded); + helper.waitUntilNavigatedTo(testTabRoot.page, () => _resetRootView(testTabRoot.entry)); const rootView2 = getRootView(); const frameStack2 = stack(); @@ -101,8 +97,7 @@ export function test_reset_tab_to_frame() { const testFrameRoot = createTestFrameRootEntry(); - _resetRootView(testFrameRoot.entry); - TKUnit.waitUntilReady(() => testFrameRoot.page.isLoaded); + helper.waitUntilNavigatedTo(testFrameRoot.page, () => _resetRootView(testFrameRoot.entry)); const rootView1 = getRootView(); const frameStack1 = stack(); @@ -113,8 +108,7 @@ export function test_reset_tab_to_frame() { export function test_reset_tab_to_tab() { const testTabRoot1 = createTestTabRootEntry(); - _resetRootView(testTabRoot1.entry); - TKUnit.waitUntilReady(() => testTabRoot1.page.isLoaded); + helper.waitUntilNavigatedTo(testTabRoot1.page, () => _resetRootView(testTabRoot1.entry)); const rootView1 = getRootView(); const frameStack1 = stack(); @@ -123,8 +117,7 @@ export function test_reset_tab_to_tab() { const testTabRoot2 = createTestTabRootEntry(); - _resetRootView(testTabRoot2.entry); - TKUnit.waitUntilReady(() => testTabRoot2.page.isLoaded); + helper.waitUntilNavigatedTo(testTabRoot2.page, () => _resetRootView(testTabRoot2.entry)); const rootView2 = getRootView(); const frameStack2 = stack(); @@ -132,6 +125,20 @@ export function test_reset_tab_to_tab() { TKUnit.assertEqual(frameStack2.length, 2); }; +export function test_reset_during_tab_index_change() { + const testTabRoot = createTestTabRootEntry(); + + helper.waitUntilNavigatedTo(testTabRoot.page, () => _resetRootView(testTabRoot.entry)); + + testTabRoot.root.selectedIndex = 1; + + const testFrameRoot = createTestFrameRootEntry(); + + helper.waitUntilNavigatedTo(testFrameRoot.page, () => _resetRootView(testFrameRoot.entry)); + + TKUnit.assertTrue(true); +} + export function tearDownModule() { // reset the root to frame for other tests const resetFrameRoot = createTestFrameRootEntry(); diff --git a/tests/app/testRunner.ts b/tests/app/testRunner.ts index fc15187cc..c698ba581 100644 --- a/tests/app/testRunner.ts +++ b/tests/app/testRunner.ts @@ -165,6 +165,9 @@ allTests["TAB-VIEW"] = tabViewTests; import * as tabViewNavigationTests from "./ui/tab-view/tab-view-navigation-tests"; allTests["TAB-VIEW-NAVIGATION"] = tabViewNavigationTests; +import * as tabViewRootTests from "./ui/tab-view/tab-view-root-tests"; +allTests["TAB-VIEW-ROOT"] = tabViewRootTests; + import * as imageTests from "./ui/image/image-tests"; allTests["IMAGE"] = imageTests; diff --git a/tests/app/ui/helper.ts b/tests/app/ui/helper.ts index 800720666..3e35df99b 100644 --- a/tests/app/ui/helper.ts +++ b/tests/app/ui/helper.ts @@ -154,6 +154,18 @@ export function getClearCurrentPage(): Page { return page; } +export function waitUntilNavigatedTo(page: Page, action: Function) { + let completed = false; + function navigatedTo(args) { + args.object.page.off("navigatedTo", navigatedTo); + completed = true; + } + + page.on("navigatedTo", navigatedTo); + action(); + TKUnit.waitUntilReady(() => completed, 100); +} + export function waitUntilNavigatedFrom(action: Function) { const currentPage = frame.topmost().currentPage; let completed = false; diff --git a/tests/app/ui/tab-view/tab-view-root-tests.ts b/tests/app/ui/tab-view/tab-view-root-tests.ts new file mode 100644 index 000000000..501df9d93 --- /dev/null +++ b/tests/app/ui/tab-view/tab-view-root-tests.ts @@ -0,0 +1,91 @@ +import * as helper from "../helper"; +import TKUnit = require("../../TKUnit"); +import { _resetRootView } from "tns-core-modules/application/"; +import { Frame, NavigationEntry } from "tns-core-modules/ui/frame"; +import { Page } from "tns-core-modules/ui/page"; +import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view"; + +export function test_whenChangingTabsWithFramesCorrectEventsAreRaised() { + const actualEventsRaised = []; + + function attachPageEventHandlers(page: Page) { + page.on(Page.loadedEvent, () => actualEventsRaised.push(`${page.id} loaded`)); + page.on(Page.unloadedEvent, () => actualEventsRaised.push(`${page.id} unloaded`)); + page.on(Page.navigatingToEvent, () => actualEventsRaised.push(`${page.id} navigatingTo`)); + page.on(Page.navigatingFromEvent, () => actualEventsRaised.push(`${page.id} navigatingFrom`)); + page.on(Page.navigatedToEvent, () => actualEventsRaised.push(`${page.id} navigatedTo`)); + page.on(Page.navigatedFromEvent, () => actualEventsRaised.push(`${page.id} navigatedFrom`)); + } + + function attachFrameEventHandlers(frame: Frame) { + frame.on(Frame.loadedEvent, () => actualEventsRaised.push(`${frame.id} loaded`)); + frame.on(Frame.unloadedEvent, () => actualEventsRaised.push(`${frame.id} unloaded`)); + } + + const page1 = new Page(); + page1.id = "Tab1 Frame1 Page1"; + attachPageEventHandlers(page1); + + const frame1 = new Frame(); + frame1.navigate(() => page1); + frame1.id = "Tab1 Frame1"; + attachFrameEventHandlers(frame1); + + const page2 = new Page(); + page2.id = "Tab2 Frame2 Page2"; + attachPageEventHandlers(page2); + + const frame2 = new Frame(); + frame2.navigate(() => page2); + frame2.id = "Tab2 Frame2"; + attachFrameEventHandlers(frame2); + + const tabView = new TabView(); + const tabEntry1 = new TabViewItem(); + tabEntry1.title = "frame1"; + tabEntry1.view = frame1; + const tabEntry2 = new TabViewItem(); + tabEntry2.title = "frame2"; + tabEntry2.view = frame2; + tabView.items = [tabEntry1, tabEntry2]; + + const entry: NavigationEntry = { + create: () => tabView + }; + + helper.waitUntilNavigatedTo(page1, () => _resetRootView(entry)); + helper.waitUntilNavigatedTo(page2, () => tabView.selectedIndex = 1); + tabView.selectedIndex = 0; + TKUnit.waitUntilReady(() => page1.isLoaded); + + const expectedEventsRaised = [ + "Tab1 Frame1 loaded", + "Tab1 Frame1 Page1 navigatingTo", + "Tab1 Frame1 Page1 loaded", + "Tab1 Frame1 Page1 navigatedTo", + "Tab1 Frame1 Page1 unloaded", + "Tab1 Frame1 unloaded", + "Tab2 Frame2 loaded", + "Tab2 Frame2 Page2 navigatingTo", + "Tab2 Frame2 Page2 loaded", + "Tab2 Frame2 Page2 navigatedTo", + "Tab2 Frame2 Page2 unloaded", + "Tab2 Frame2 unloaded", + "Tab1 Frame1 Page1 loaded", + "Tab1 Frame1 loaded" + ]; + + TKUnit.arrayAssert(actualEventsRaised, expectedEventsRaised); +} + +export function tearDownModule() { + const page = new Page(); + const frame = new Frame(); + frame.navigate(() => page); + + const entry: NavigationEntry = { + create: () => frame + }; + + helper.waitUntilNavigatedTo(page, () => _resetRootView(entry)); +} diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index 3ab4d17d7..13315258c 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -201,9 +201,7 @@ export class Frame extends FrameBase { const currentEntryChanged = current !== entry; if (currentEntryChanged) { this._updateBackstack(entry, isBack); - } - if (currentEntryChanged) { // If activity was destroyed we need to destroy fragment and UI // of current and new entries. if (this._tearDownPending) { @@ -936,7 +934,9 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks { public resetActivityContent(activity: android.app.Activity): void { if (this._rootView) { - // if we already have a root view, we reset it. + const manager = this._rootView._getFragmentManager(); + manager.executePendingTransactions(); + this._rootView._onRootViewReset(); } // Delete previously cached root view in order to recreate it. diff --git a/tns-core-modules/ui/frame/frame.ios.ts b/tns-core-modules/ui/frame/frame.ios.ts index 070da5f63..e212dee8d 100644 --- a/tns-core-modules/ui/frame/frame.ios.ts +++ b/tns-core-modules/ui/frame/frame.ios.ts @@ -38,11 +38,13 @@ export class Frame extends FrameBase { } public setCurrent(entry: BackstackEntry, isBack: boolean): void { - if (entry !== this._currentEntry) { + const current = this._currentEntry; + const currentEntryChanged = current !== entry; + if (currentEntryChanged) { this._updateBackstack(entry, isBack); - } - super.setCurrent(entry, isBack); + super.setCurrent(entry, isBack); + } } @profile diff --git a/tns-core-modules/ui/tab-view/tab-view.ios.ts b/tns-core-modules/ui/tab-view/tab-view.ios.ts index 67da564c4..981f60431 100644 --- a/tns-core-modules/ui/tab-view/tab-view.ios.ts +++ b/tns-core-modules/ui/tab-view/tab-view.ios.ts @@ -32,7 +32,7 @@ class UITabBarControllerImpl extends UITabBarController { public viewWillAppear(animated: boolean): void { super.viewWillAppear(animated); const owner = this._owner.get(); - if(!owner){ + if (!owner) { return; } @@ -147,7 +147,7 @@ function updateItemIconPosition(tabBarItem: UITabBarItem): void { export class TabViewItem extends TabViewItemBase { private __controller: UIViewController; - + public setViewController(controller: UIViewController, nativeView: UIView) { this.__controller = controller; this.setNativeView(nativeView); @@ -295,7 +295,7 @@ export class TabView extends TabViewBase { private getViewController(item: TabViewItem): UIViewController { let newController: UIViewController = item.view ? item.view.viewController : null; - + if (newController) { item.setViewController(newController, newController.view); return newController; @@ -399,6 +399,7 @@ export class TabView extends TabViewBase { } if (value > -1) { + (this._ios)._willSelectViewController = this._ios.viewControllers[value]; this._ios.selectedIndex = value; } }