From e31498c4b8e6c5c2758e699e418daf72cac50ff4 Mon Sep 17 00:00:00 2001 From: Dan Bucholtz Date: Wed, 27 Apr 2016 11:49:58 -0500 Subject: [PATCH] fix(navigation): revert previous navigation fix (efb9bd) and added a different fix for the infite lo revert previous navigation fix (efb9bd) and added a different fix for the infite loop issue. Wrote additional tests to verify the fix --- js/angular/service/history.js | 12 +- test/unit/angular/service/history.unit.js | 231 ++++++++++++++++++++-- 2 files changed, 221 insertions(+), 22 deletions(-) diff --git a/js/angular/service/history.js b/js/angular/service/history.js index 5b76a6b5cdf..bacaabe06af 100644 --- a/js/angular/service/history.js +++ b/js/angular/service/history.js @@ -277,6 +277,16 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $ // it's back view would be better represented using the current view as its back view tmp = getViewById(switchToView.backViewId); if (tmp && switchToView.historyId !== tmp.historyId) { + // the new view is being removed from it's old position in the history and being placed at the top, + // so we need to update any views that reference it as a backview, otherwise there will be infinitely loops + var viewIds = Object.keys(viewHistory.views); + viewIds.forEach(function(viewId) { + var view = viewHistory.views[viewId]; + if ( view.backViewId === switchToView.viewId ) { + view.backViewId = null; + } + }); + hist.stack[hist.cursor].backViewId = currentView.viewId; } @@ -355,7 +365,7 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $ viewId: viewId, index: hist.stack.length, historyId: hist.historyId, - backViewId: (currentView && currentView.viewId && (currentView.historyId === hist.historyId || currentView.historyId === hist.parentHistoryId) ? currentView.viewId : null), + backViewId: (currentView && currentView.viewId ? currentView.viewId : null), forwardViewId: null, stateId: currentStateId, stateName: this.currentStateName(), diff --git a/test/unit/angular/service/history.unit.js b/test/unit/angular/service/history.unit.js index 4df078dc478..01b22900b17 100644 --- a/test/unit/angular/service/history.unit.js +++ b/test/unit/angular/service/history.unit.js @@ -5,19 +5,19 @@ describe('Ionic History', function() { stateProvider = $stateProvider; $stateProvider - .state('home', { url: "/" }) - .state('home.item', { url: "front/:id" }) + .state('home', { url: '/' }) + .state('home.item', { url: 'front/:id' }) - .state('about', { url: "/about" }) - .state('about.person', { url: "/person" }) - .state('about.person.item', { url: "/id" }) + .state('about', { url: '/about' }) + .state('about.person', { url: '/person' }) + .state('about.person.item', { url: '/id' }) .state('about.sidebar', {}) .state('about.sidebar.item', {}) - .state('contact', { url: "/contact" }) + .state('contact', { url: '/contact' }) - .state('info', { url: "/info" }) + .state('info', { url: '/info' }) .state('tabs', { abstract: true }) .state('tabs.tab1view1', {}) @@ -231,9 +231,10 @@ describe('Ionic History', function() { currentView = ionicHistory.currentView(); backView = ionicHistory.backView(); forwardView = ionicHistory.forwardView(); - expect(currentView.backViewId).toEqual(null); + expect(currentView.backViewId).toEqual(tab1view1Reg.viewId); expect(currentView.forwardViewId).toEqual(null); - expect(backView).toEqual(null); + expect(backView.viewId).toEqual(tab1view1Reg.viewId); + expect(backView.forwardViewId).toEqual(currentView.viewId); expect(ionicHistory.viewHistory().histories.root.cursor).toEqual(2); expect(ionicHistory.viewHistory().histories.root.stack.length).toEqual(3); @@ -291,7 +292,7 @@ describe('Ionic History', function() { expect(homeReg2.action).toEqual('moveBack'); expect(homeReg2.direction).toEqual('back'); - // this should overwrite that we went to the "about" view + // this should overwrite that we went to the 'about' view contactScope = {}; $state.go('contact'); rootScope.$apply(); @@ -644,7 +645,7 @@ describe('Ionic History', function() { // go back to tab1, and it should load the first view of tab1 expect(tab1Scope.$historyId).toEqual(orgTab1HistoryId); - rootScope.$broadcast("$ionicHistory.change", { historyId: tab1Scope.$historyId, enableUrlChange: false }); + rootScope.$broadcast('$ionicHistory.change', { historyId: tab1Scope.$historyId, enableUrlChange: false }); rootScope.$apply(); expect(ionicHistory.currentStateName()).toEqual('tabs.tab1view1'); registerData = ionicHistory.register(tab1view1Scope, false); @@ -688,11 +689,11 @@ describe('Ionic History', function() { expect(registerData.action).toEqual('moveBack'); expect(registerData.direction).toEqual('swap'); currentView = ionicHistory.currentView(); - expect(currentView.backViewId).toEqual(null); + expect(currentView.backViewId).toEqual(tab1view2ViewId); expect(currentView.forwardViewId).toEqual(null); // should be remembered at the tab 1 view 2 - rootScope.$broadcast("$ionicHistory.change", { historyId: tab1Scope.$historyId }); + rootScope.$broadcast('$ionicHistory.change', { historyId: tab1Scope.$historyId }); rootScope.$apply(); expect(ionicHistory.currentStateName()).toEqual('tabs.tab1view2'); expect(ionicHistory.viewHistory().histories[tab1Scope.$historyId].cursor).toEqual(1); @@ -1293,23 +1294,23 @@ describe('Ionic History', function() { it('should change history on event changeHistory', inject(function($location, $state) { $location.url('/original'); - rootScope.$broadcast("$ionicHistory.change"); + rootScope.$broadcast('$ionicHistory.change'); expect($location.url()).toEqual('/original'); - rootScope.$broadcast("$ionicHistory.change", { uiSref: 'about' }); + rootScope.$broadcast('$ionicHistory.change', { uiSref: 'about' }); expect($location.url()).toEqual('/about'); - rootScope.$broadcast("$ionicHistory.change", { url: '/url' }); + rootScope.$broadcast('$ionicHistory.change', { url: '/url' }); expect($location.url()).toEqual('/url'); ionicHistory.viewHistory().histories.h123 = { stack: [], cursor: -1 }; - rootScope.$broadcast("$ionicHistory.change", { historyId: 'h123' }); + rootScope.$broadcast('$ionicHistory.change', { historyId: 'h123' }); expect($location.url()).toEqual('/url'); var newView = ionicHistory.createView({ stateName: 'about' }); ionicHistory.viewHistory().histories.h123.stack.push(newView); ionicHistory.viewHistory().histories.h123.cursor++; - rootScope.$broadcast("$ionicHistory.change", { historyId: 'h123' }); + rootScope.$broadcast('$ionicHistory.change', { historyId: 'h123' }); rootScope.$apply(); expect($state.current.name).toEqual('about'); })); @@ -1317,13 +1318,13 @@ describe('Ionic History', function() { it('should update document title', inject(function($document) { $document[0].title = 'Original Title'; - rootScope.$broadcast("$ionicView.afterEnter"); + rootScope.$broadcast('$ionicView.afterEnter'); expect($document[0].title).toEqual('Original Title'); - rootScope.$broadcast("$ionicView.afterEnter", {}); + rootScope.$broadcast('$ionicView.afterEnter', {}); expect($document[0].title).toEqual('Original Title'); - rootScope.$broadcast("$ionicView.afterEnter", { title: 'New Title' }); + rootScope.$broadcast('$ionicView.afterEnter', { title: 'New Title' }); expect($document[0].title).toEqual('New Title'); })); @@ -1358,4 +1359,192 @@ describe('Ionic History', function() { })); + it('should wipe out any back view references to a view when revisiting that view', inject(function($state, $ionicHistory) { + + var tab1Container = {}; + var tab2Container = {}; + var tab3Container = {}; + ionicHistory.registerHistory(tab1Container); + ionicHistory.registerHistory(tab2Container); + ionicHistory.registerHistory(tab3Container); + + // register tab1, view1 + $state.go('tabs.tab1view1'); + rootScope.$apply(); + var tab1view1Reg = ionicHistory.register(tab1Container, false); + expect(ionicHistory.viewHistory().histories[tab1Container.$historyId].cursor).toEqual(0); + + // register tab3, view1 + $state.go('tabs.tab3view1'); + rootScope.$apply(); + var tab3view1Reg = ionicHistory.register(tab3Container, false); + + // register tab2, view1 + $state.go('tabs.tab2view1'); + rootScope.$apply(); + var tab2view1Reg = ionicHistory.register(tab2Container, false); + + // go to tab2, view 2 + // register tab1, view2 + $state.go('tabs.tab2view2'); + rootScope.$apply(); + var tab2view2Reg = ionicHistory.register(tab2Container, false); + expect(ionicHistory.viewHistory().histories[tab2Container.$historyId].cursor).toEqual(1); + currentView = ionicHistory.currentView(); + expect(currentView.backViewId).toEqual(tab2view1Reg.viewId); + + // register tab3, view1 + $state.go('tabs.tab3view1'); + rootScope.$apply(); + tab3view1Reg = ionicHistory.register(tab3Container, false); + + currentView = ionicHistory.currentView(); + expect(currentView.backViewId).toEqual(tab2view2Reg.viewId); + + backView = ionicHistory.getViewById(currentView.backViewId); + expect(backView.backViewId).toEqual(tab2view1Reg.viewId); + + var backBackView = ionicHistory.getViewById(backView.backViewId); + expect(backBackView.backViewId).toEqual(null); + + })); + + it('should be able to go forward from non-tabbed view, to tabbed-view, to different non-tabbed view, and then all the way back', inject(function($state, $ionicHistory) { + + $state.go('home'); + rootScope.$apply(); + var view1Scope = {}; + var rsp = ionicHistory.register(view1Scope, false); + + var tab1Container = {}; + ionicHistory.registerHistory(tab1Container); + + // register tab1, view1 + $state.go('tabs.tab1view1'); + rootScope.$apply(); + var tab1view1Reg = ionicHistory.register(tab1Container, false); + expect(ionicHistory.viewHistory().histories[tab1Container.$historyId].cursor).toEqual(0); + + $state.go('about'); + rootScope.$apply(); + rsp = ionicHistory.register({}, false); + + var currentView = ionicHistory.currentView(); + var backView = ionicHistory.getViewById(currentView.backViewId); + + expect(currentView.stateName).toEqual('about'); + expect(backView.stateName).toEqual('tabs.tab1view1'); + + var originalView = ionicHistory.getViewById(backView.backViewId); + expect(originalView.stateName).toEqual('home'); + + })); + + it('should be able to go forward from non-tabbed view, to multiple tabbed-views, to different non-tabbed view, and then all the way back', inject(function($state, $ionicHistory) { + + $state.go('home'); + rootScope.$apply(); + var view1Scope = {}; + var rsp = ionicHistory.register(view1Scope, false); + + var tab1Container = {}; + var tab2Container = {}; + var tab3Container = {}; + ionicHistory.registerHistory(tab1Container); + ionicHistory.registerHistory(tab2Container); + ionicHistory.registerHistory(tab3Container); + + // register tab1, view1 + $state.go('tabs.tab1view1'); + rootScope.$apply(); + var tab1view1Reg = ionicHistory.register(tab1Container, false); + expect(ionicHistory.viewHistory().histories[tab1Container.$historyId].cursor).toEqual(0); + + $state.go('tabs.tab2view1'); + rootScope.$apply(); + var tab2view1Reg = ionicHistory.register(tab2Container, false); + expect(ionicHistory.viewHistory().histories[tab2Container.$historyId].cursor).toEqual(0); + + $state.go('tabs.tab2view2'); + rootScope.$apply(); + var tab2view2Reg = ionicHistory.register(tab2Container, false); + expect(ionicHistory.viewHistory().histories[tab2Container.$historyId].cursor).toEqual(1); + + $state.go('tabs.tab3view1'); + rootScope.$apply(); + var tab3view1Reg = ionicHistory.register(tab3Container, false); + expect(ionicHistory.viewHistory().histories[tab3Container.$historyId].cursor).toEqual(0); + + $state.go('tabs.tab3view2'); + rootScope.$apply(); + var tab3view2Reg = ionicHistory.register(tab3Container, false); + expect(ionicHistory.viewHistory().histories[tab3Container.$historyId].cursor).toEqual(1); + + $state.go('about'); + rootScope.$apply(); + rsp = ionicHistory.register({}, false); + + var currentView = ionicHistory.currentView(); + var backView = ionicHistory.getViewById(currentView.backViewId); + + expect(currentView.stateName).toEqual('about'); + expect(backView.stateName).toEqual('tabs.tab3view2'); + + // update the backview + backView = ionicHistory.getViewById(backView.backViewId); + expect(backView.stateName).toEqual('tabs.tab3view1'); + + backView = ionicHistory.getViewById(backView.backViewId); + expect(backView.stateName).toEqual('tabs.tab2view2'); + + backView = ionicHistory.getViewById(backView.backViewId); + expect(backView.stateName).toEqual('tabs.tab2view1'); + + backView = ionicHistory.getViewById(backView.backViewId); + expect(backView.stateName).toEqual('tabs.tab1view1'); + + backView = ionicHistory.getViewById(backView.backViewId); + expect(backView.stateName).toEqual('home'); + + })); + + it('should navigate forward then be able to get back via backViews', inject(function($state, $ionicHistory) { + + $state.go('home'); + rootScope.$apply(); + var homeScope = {}; + var home = ionicHistory.register(homeScope, false); + + $state.go('about'); + rootScope.$apply(); + var aboutScope = {}; + var about = ionicHistory.register(aboutScope, false); + + $state.go('contact'); + rootScope.$apply(); + var contactScope = {}; + var contact = ionicHistory.register(contactScope, false); + + $state.go('home'); + rootScope.$apply(); + var homeScope = {}; + var home = ionicHistory.register(homeScope, false); + + var currentView = ionicHistory.currentView(); + expect(currentView.stateName).toEqual('home'); + + var backView = ionicHistory.getViewById(currentView.backViewId); + expect(backView.stateName).toEqual('contact'); + + // update the backview + backView = ionicHistory.getViewById(backView.backViewId); + expect(backView.stateName).toEqual('about'); + + // update the backview + backView = ionicHistory.getViewById(backView.backViewId); + expect(backView.stateName).toEqual('home'); + expect(backView.backViewId).toEqual(null); + + })); + });