Files
ionic-framework/test/unit/angular/directive/navView.unit.js
2015-02-16 14:40:23 -06:00

1331 lines
41 KiB
JavaScript

describe('Ionic nav-view', function() {
beforeEach(module('ionic'));
var compile, $rootScope, elem;
var aState = {
template: 'aState template'
},
bState = {
template: 'bState template'
},
cState = {
views: {
'cview': {
template: 'cState cview template'
}
}
},
dState = {
views: {
'dview1': {
template: 'dState dview1 template'
},
'dview2': {
template: 'dState dview2 template'
}
}
},
eUiViewState = {
template: '<div ui-view="eview" class="eview"></div>'
},
eNavViewState = {
template: '<ion-nav-view name="eview" class="eview"></ion-nav-view>'
},
fUiViewState = {
views: {
'eview': {
template: 'fState eview template'
}
}
},
fNavViewState = {
views: {
'eview': {
template: 'fState eview template'
}
}
},
gUiViewState = {
template: '<div ui-view="inner"><span>{{content}}</span></div>'
},
hUiViewState = {
views: {
'inner': {
template: 'hUiViewState inner template'
}
}
},
gNavViewState = {
template: '<ion-nav-view name="inner"><span>{{content}}</span></ion-nav-view>'
},
hNavViewState = {
views: {
'inner': {
template: 'hNavViewState inner template'
}
}
},
iState = {
template: '<ion-nav-view>'+
'<ul><li ng-repeat="item in items">{{item}}</li></ul>'+
'</ion-nav-view>'
},
jState = {
template: 'jState'
},
page1State = {
template: 'page1'
},
page2State = {
template: 'page2'
},
page3State = {
template: 'page3'
},
page4State = {
template: 'page4'
},
page5State = {
template: 'page5'
},
ionView1State = {
template: '<ion-view>ionView1</ion-view>'
},
ionView2State = {
template: '<ion-view>ionView2</ion-view>'
},
ionViewCacheFalseAttrState = {
template: '<ion-view cache-view="false">ionViewCacheFalseAttr</ion-view>'
},
ionViewCacheFalsePropertyState = {
template: '<ion-view>ionViewCacheFalsePropertyState</ion-view>',
cache: false
},
rootView1State = {
views: {
'root': {
template: '<ion-view>rootView1State</ion-view>'
}
}
},
rootView2State = {
views: {
'root': {
template: '<ion-view>rootView2State</ion-view>'
}
}
},
rootView1NoCacheState = {
cache: 'false',
views: {
'root': {
template: '<ion-view>rootView1NoCacheState</ion-view>'
}
}
},
tabAbstractState = {
abstract: true,
views: {
'root': {
template: '<ion-tabs>' +
'<ion-tab><ion-nav-view name="tab1"></ion-nav-view></ion-tab>' +
'<ion-tab><ion-nav-view name="tab2"></ion-nav-view></ion-tab>' +
'<ion-tab><ion-nav-view name="tab3"></ion-nav-view></ion-tab>' +
'<ion-tab><ion-view><h2>Inline Tab</h2></ion-view></ion-tab>' +
'</ion-tabs>'
}
}
},
tab1page1State = {
views: {
'tab1': {
template: 'tab1page1'
}
}
},
tab1page2State = {
views: {
'tab1': {
template: 'tab1page2'
}
}
},
tab2page1State = {
views: {
'tab2': {
template: 'tab2page1'
}
}
},
tab3page1State = {
views: {
'tab3': {
template: 'tab3page1'
}
}
},
tab3page2NoCacheState = {
cache: false,
views: {
'tab3': {
template: 'tab3page2NoCache'
}
}
}
beforeEach(module(function ($stateProvider) {
$stateProvider
.state('a', aState)
.state('b', bState)
.state('c', cState)
.state('d', dState)
.state('eUiView', eUiViewState)
.state('eNavView', eNavViewState)
.state('eUiView.f', fUiViewState)
.state('eNavView.f', fNavViewState)
.state('gUiView', gUiViewState)
.state('gUiView.hUiView', hUiViewState)
.state('gNavView', gNavViewState)
.state('gNavView.hNavView', hNavViewState)
.state('i', iState)
.state('j', jState)
.state('page1', page1State)
.state('page2', page2State)
.state('page3', page3State)
.state('page4', page4State)
.state('page5', page5State)
.state('ionView1', ionView1State)
.state('ionView2', ionView2State)
.state('ionViewCacheFalseAttr', ionViewCacheFalseAttrState)
.state('ionViewCacheFalseProperty', ionViewCacheFalsePropertyState)
.state('tabAbstract', tabAbstractState)
.state('tabAbstract.tab1page1', tab1page1State)
.state('tabAbstract.tab1page2', tab1page2State)
.state('tabAbstract.tab2page1', tab2page1State)
.state('tabAbstract.tab3page1', tab3page1State)
.state('tabAbstract.tab3page2', tab3page2NoCacheState)
.state('rootView1', rootView1State)
.state('rootView2', rootView2State)
.state('rootView1NoCache', rootView1NoCacheState);
}));
beforeEach(inject(function(_$compile_, $ionicConfig, $rootScope) {
$compile = _$compile_;
scope = $rootScope.$new();
elem = angular.element('<div>');
ionic.Platform.setPlatform('ios');
$ionicConfig.views.maxCache(30);
$ionicConfig.views.forwardCache(false);
ionic.requestAnimationFrame = function(cb){cb()};
}));
it('should publish a controller', function() {
var view = angular.element('<ion-nav-view></ion-nav-view>');
$compile(view)(scope);
scope.$apply();
expect(view.controller('ionNavView')).toBeTruthy();
});
it('anonymous ui-view should be replaced with the template of the current $state', inject(function ($state, $q) {
elem.append($compile('<div><ui-view>ui view</ui-view></div>')(scope));
expect(elem.find('ui-view').text()).toBe('ui view');
$state.go(aState);
$q.flush();
expect(elem.find('ui-view').text()).toBe(aState.template);
}));
it('anonymous ion-nav-view should be replaced with the template of the current $state', inject(function ($state, $q) {
elem.append($compile('<div><ion-nav-view>nav view--</ion-nav-view></div>')(scope));
expect(elem.find('ion-nav-view').text()).toBe('nav view--');
$state.go(aState);
$q.flush();
expect(elem.find('ion-nav-view').text()).toBe('nav view--' + aState.template);
}));
it('named ion-nav-view should be replaced with the template of the current $state', inject(function ($state, $q) {
elem.append($compile('<div><ion-nav-view name="cview"></ion-nav-view</div>')(scope));
$state.go(cState);
$q.flush();
expect(elem.find('ion-nav-view').text()).toBe(cState.views.cview.template);
}));
it('ion-nav-view should be updated after transition to another state', inject(function ($state, $q) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
expect(elem.find('ion-nav-view').text()).toBe('');
$state.go(aState);
$q.flush();
expect(elem.find('ion-nav-view').text()).toBe(aState.template);
$state.go(bState);
$q.flush();
expect(elem.find('ion-nav-view').text()).toBe(aState.template + bState.template);
}));
it('should handle NOT nested ion-nav-view', inject(function ($state, $q, $timeout) {
elem.append($compile('<div><ion-nav-view name="dview1" class="dview1"></ion-nav-view><ion-nav-view name="dview2" class="dview2"></ion-nav-view></div>')(scope));
expect(elem.find('ion-nav-view').eq(0).text()).toBe('');
expect(elem.find('ion-nav-view').eq(1).text()).toBe('');
$state.go(dState);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').eq(0).text()).toBe(dState.views.dview1.template);
expect(elem.find('ion-nav-view').eq(1).text()).toBe(dState.views.dview2.template);
}));
it('should handle nested ui-views (testing two levels deep)', inject(function ($state, $q, $timeout) {
$compile(elem.append('<div><ui-view></ui-view></div>'))(scope);
expect(elem.find('ui-view').text()).toBe('');
$state.go(fUiViewState);
$q.flush();
expect(elem.find('ui-view').text()).toBe(fUiViewState.views.eview.template);
}));
it('should handle nested ion-nav-view (testing two levels deep)', inject(function ($state, $q, $timeout) {
$compile(elem.append('<div><ion-nav-view></ion-nav-view></div>'))(scope);
expect(elem.find('ion-nav-view').text()).toBe('');
$state.go(fNavViewState);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').text()).toBe(fNavViewState.views.eview.template + fNavViewState.views.eview.template);
}));
it('initial view should be compiled if the view is empty', inject(function ($state, $q, $timeout) {
var content = 'inner content';
scope.content = content;
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(gNavViewState);
$q.flush();
$timeout.flush();
expect(elem.eq(0).find('ion-nav-view').find('ion-nav-view').text()).toBe(content);
}));
it('initial view should be put back after removal of the view', inject(function ($state, $q) {
var content = 'inner content';
scope.content = content;
elem.append($compile('<div><ui-view></ui-view></div>')(scope));
$state.go(hUiViewState);
$q.flush();
expect(elem.find('ui-view').text()).toBe(hUiViewState.views.inner.template);
// going to the parent state which makes the inner view empty
$state.go(gUiViewState);
$q.flush();
expect(elem.find('ui-view').text()).toBe(content);
}));
// ION-NAV-VIEW CANNOT DO THIS!!!!!!
// it('initial view should be put back after removal of the view', inject(function ($state, $q) {
// var content = 'inner content';
// scope.content = content;
// elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
// $state.go(hNavViewState);
// $q.flush();
// var orgElement = elem.find('div').find('ion-nav-view').find('ion-nav-view').find('span');
// expect(orgElement.text()).toBe(content);
// expect(orgElement.hasClass('nav-view-cache')).toBe(true);
// var newElement = elem.find('div').find('ion-nav-view').find('ion-nav-view').find('div');
// expect(newElement.text()).toBe(hNavViewState.views.inner.template);
// expect(newElement.hasClass('nav-view-cache')).toBe(false);
// // going to the parent state which makes the inner view empty
// $state.go(gNavViewState);
// $q.flush();
// expect(elem.find('ion-nav-view').text()).toBe(content);
// }));
it('initial view should be transcluded once to prevent breaking other directives', inject(function ($rootScope, $state, $q, $timeout) {
scope.items = ["list", "of", "items"];
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
// transition to state that has an initial view
$state.go(iState);
$q.flush();
$timeout.flush();
// verify if ng-repeat has been compiled
expect(elem.find('li').length).toBe(scope.items.length);
// transition to another state that replace the initial content
$state.go(jState);
$q.flush();
$timeout.flush();
expect(elem[0].querySelector('[nav-view="active"]').innerText).toBe(jState.template);
// transition back to the state with empty subview and the initial view
$state.go(iState);
$q.flush();
$timeout.flush();
// verify if the initial view is correct
expect(elem[0].querySelectorAll('[nav-view="active"] li').length).toBe(scope.items.length);
expect(elem.find('li').length).toBe(scope.items.length);
// change scope properties
scope.$apply(function () {
scope.items.push(".", "Working?");
});
// verify if the initial view has been updated
expect(elem.find('li').length).toBe(scope.items.length);
}));
it('should handle ion-nav-view inside ng-if', inject(function ($state, $q, $compile) {
scope.someBoolean = false;
elem.append($compile('<div ng-if="someBoolean"><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(aState);
$q.flush();
// Verify there is no ion-nav-view in the DOM
expect(elem.find('ion-nav-view').length).toBe(0);
// Turn on the div that holds the ui-view
scope.someBoolean = true;
scope.$digest();
// Verify that the ion-nav-view is there and it has the correct content
expect(elem.find('ion-nav-view').text()).toBe(aState.template);
// Turn off the ui-view
scope.someBoolean = false;
scope.$digest();
// Verify there is ion-nav ion-nav-view in the DOM
expect(elem.find('ion-nav-view').length).toBe(0);
// Turn on the div that holds the ion-nav-view once again
scope.someBoolean = true;
scope.$digest();
// Verify that the ui-view is there and it has the correct content
expect(elem.find('ion-nav-view').text()).toBe(aState.template);
}));
it('should add multiple ion-nav-view', inject(function ($state, $q, $timeout, $compile) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
expect(elem.find('ion-nav-view').find('div').length).toBe(0);
$state.go(page1State);
$q.flush();
$timeout.flush();
var divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(1);
expect(divs.eq(0).attr('nav-view')).toBe('active');
expect(divs.eq(0).text()).toBe('page1');
$state.go(page2State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(2);
expect(divs.eq(0).attr('nav-view')).toBe('cached');
expect(divs.eq(0).text()).toBe('page1');
expect(divs.eq(1).attr('nav-view')).toBe('active');
expect(divs.eq(1).text()).toBe('page2');
$state.go(page3State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(3);
expect(divs.eq(0).attr('nav-view')).toBe('cached');
expect(divs.eq(0).text()).toBe('page1');
expect(divs.eq(1).attr('nav-view')).toBe('cached');
expect(divs.eq(1).text()).toBe('page2');
expect(divs.eq(2).attr('nav-view')).toBe('active');
expect(divs.eq(2).text()).toBe('page3');
$state.go(page4State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(4);
expect(divs.eq(0).attr('nav-view')).toBe('cached');
expect(divs.eq(0).text()).toBe('page1');
expect(divs.eq(1).attr('nav-view')).toBe('cached');
expect(divs.eq(1).text()).toBe('page2');
expect(divs.eq(2).attr('nav-view')).toBe('cached');
expect(divs.eq(2).text()).toBe('page3');
expect(divs.eq(3).attr('nav-view')).toBe('active');
expect(divs.eq(3).text()).toBe('page4');
}));
it('should remove ion-nav-views when going back', inject(function ($state, $q, $timeout, $compile) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(page1State);
$q.flush();
$timeout.flush();
$state.go(page2State);
$q.flush();
$timeout.flush();
$state.go(page3State);
$q.flush();
$timeout.flush();
$state.go(page4State);
$q.flush();
$timeout.flush();
var divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(4);
expect(divs.eq(0).attr('nav-view')).toBe('cached');
expect(divs.eq(1).attr('nav-view')).toBe('cached');
expect(divs.eq(2).attr('nav-view')).toBe('cached');
expect(divs.eq(3).attr('nav-view')).toBe('active');
$state.go(page3State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(3);
expect(divs.eq(0).attr('nav-view')).toBe('cached');
expect(divs.eq(1).attr('nav-view')).toBe('cached');
expect(divs.eq(2).attr('nav-view')).toBe('active');
$state.go(page2State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(2);
expect(divs.eq(0).attr('nav-view')).toBe('cached');
expect(divs.eq(1).attr('nav-view')).toBe('active');
$state.go(page1State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(1);
expect(divs.eq(0).attr('nav-view')).toBe('active');
}));
it('should disconnect and reconnect view scopes', inject(function ($state, $q, $timeout, $compile) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(page1State);
$q.flush();
$timeout.flush();
var divs = elem.find('ion-nav-view').find('div');
expect(divs.eq(0).scope().$$disconnected).toBeUndefined();
$state.go(page2State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.eq(0).scope().$$disconnected).toBe(true);
expect(divs.eq(1).scope().$$disconnected).toBeUndefined();
$state.go(page1State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.eq(0).scope().$$disconnected).toBe(false);
}));
it('should have connected scopes at the time of lifecycle events', inject(function ($state, $q, $timeout, $compile) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(page1State);
$q.flush();
$timeout.flush();
var beforeEnterDisconnected, afterEnterDisconnected, beforeLeaveDisconnected, afterLeaveDisconnected;
scope.$on('$ionicView.beforeEnter', function(ev, d){
beforeEnterDisconnected = elem.find('ion-nav-view').find('div').eq(1).scope().$$disconnected;
});
scope.$on('$ionicView.afterEnter', function(ev, d){
afterEnterDisconnected = elem.find('ion-nav-view').find('div').eq(1).scope().$$disconnected;
});
scope.$on('$ionicView.beforeLeave', function(ev, d){
beforeLeaveDisconnected = elem.find('ion-nav-view').find('div').eq(0).scope().$$disconnected;
});
scope.$on('$ionicView.afterLeave', function(ev, d){
afterLeaveDisconnected = elem.find('ion-nav-view').find('div').eq(0).scope().$$disconnected;
});
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(beforeEnterDisconnected).toBeUndefined();
expect(afterEnterDisconnected).toBeUndefined();
expect(beforeLeaveDisconnected).toBeUndefined();
expect(afterLeaveDisconnected).toBeUndefined();
expect(elem.find('ion-nav-view').find('div').eq(0).scope().$$disconnected).toBe(true);
expect(elem.find('ion-nav-view').find('div').eq(1).scope().$$disconnected).toBeUndefined();
}));
it('should not cache ion-nav-views that were forward when moving back', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
var unloaded;
scope.$on('$ionicView.unloaded', function(ev, d){
unloaded = d;
});
$ionicConfig.views.maxCache(2);
$state.go(page1State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(1);
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(2);
$state.go(page3State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(3);
expect(unloaded).toBeUndefined();
$state.go(page4State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(3);
expect(unloaded.historyId).toBe('root');
expect(unloaded.stateName).toBe('page1');
$state.go(page3State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(2);
expect(unloaded.stateName).toBe('page4');
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(1);
expect(unloaded.stateName).toBe('page3');
$state.go(page1State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(1);
expect(unloaded.stateName).toBe('page2');
}));
it('should cache ion-nav-views that were forward when moving back with $ionicConfig.cacheForwardViews=true', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$ionicConfig.views.forwardCache(true);
$state.go(page1State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(1);
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(2);
$state.go(page3State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(3);
$state.go(page4State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(4);
$state.go(page3State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(4);
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(4);
$state.go(page1State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('div').length).toBe(4);
}));
it('should not cache ion-views with the cache-view="true" attribute', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(ionView1State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('ion-view').length).toBe(1);
expect(elem.find('ion-nav-view').find('ion-view').eq(0).text()).toBe('ionView1');
$state.go(ionViewCacheFalseAttrState);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('ion-view').length).toBe(2);
expect(elem.find('ion-nav-view').find('ion-view').eq(0).text()).toBe('ionView1');
expect(elem.find('ion-nav-view').find('ion-view').eq(1).text()).toBe('ionViewCacheFalseAttr');
$state.go(ionView2State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('ion-view').length).toBe(2);
expect(elem.find('ion-nav-view').find('ion-view').eq(0).text()).toBe('ionView1');
expect(elem.find('ion-nav-view').find('ion-view').eq(1).text()).toBe('ionView2');
}));
it('should not cache ion-views with the cache=false state property', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(ionView1State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('ion-view').length).toBe(1);
expect(elem.find('ion-nav-view').find('ion-view').eq(0).text()).toBe('ionView1');
$state.go(ionViewCacheFalsePropertyState);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('ion-view').length).toBe(2);
expect(elem.find('ion-nav-view').find('ion-view').eq(0).text()).toBe('ionView1');
expect(elem.find('ion-nav-view').find('ion-view').eq(1).text()).toBe('ionViewCacheFalsePropertyState');
$state.go(ionView2State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('ion-view').length).toBe(2);
expect(elem.find('ion-nav-view').find('ion-view').eq(0).text()).toBe('ionView1');
expect(elem.find('ion-nav-view').find('ion-view').eq(1).text()).toBe('ionView2');
}));
it('should remove the oldest accessed view (not the oldest, but oldest accessed)', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$ionicConfig.views.maxCache(3);
$state.go(page1State);
$q.flush();
$timeout.flush();
var divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(1);
expect(divs.eq(0).text()).toBe('page1');
$state.go(page2State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(2);
expect(divs.eq(1).text()).toBe('page2');
$state.go(page3State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(3);
expect(divs.eq(2).text()).toBe('page3');
$state.go(page4State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(4);
$state.go(page1State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(4);
$state.go(page5State);
$q.flush();
$timeout.flush();
divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(4);
expect(divs.eq(0).text()).toBe('page1');
expect(divs.eq(1).text()).toBe('page3');
expect(divs.eq(2).text()).toBe('page4');
expect(divs.eq(3).text()).toBe('page5');
}));
it('should emit $ionicView loaded event only once if cached', inject(function ($state, $q, $timeout, $compile) {
var loaded;
scope.$on('$ionicView.loaded', function(ev, d){
loaded = d;
});
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(page1State);
$q.flush();
$timeout.flush();
expect(loaded.stateName).toEqual('page1');
loaded = null;
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(loaded.stateName).toEqual('page2');
loaded = null;
$state.go(page1State);
$q.flush();
$timeout.flush();
expect(loaded).toEqual(null);
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(loaded.stateName).toEqual('page2');
loaded = null;
}));
it('should emit $ionicView enter events', inject(function ($state, $q, $timeout, $compile) {
var beforeEnter, afterEnter, enter;
scope.$on('$ionicView.beforeEnter', function(ev, d){
beforeEnter = d;
});
scope.$on('$ionicView.afterEnter', function(ev, d){
afterEnter = d;
});
scope.$on('$ionicView.enter', function(ev, d){
enter = d;
});
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(page1State);
$q.flush();
$timeout.flush();
expect(beforeEnter.stateName).toEqual('page1');
expect(afterEnter.stateName).toEqual('page1');
expect(enter.stateName).toEqual('page1');
expect(enter.fromCache).toEqual(false);
expect(enter.transitionId).toEqual(1);
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(beforeEnter.stateName).toEqual('page2');
expect(afterEnter.stateName).toEqual('page2');
expect(enter.stateName).toEqual('page2');
expect(enter.fromCache).toEqual(false);
expect(enter.transitionId).toEqual(2);
$state.go(page1State);
$q.flush();
$timeout.flush();
expect(beforeEnter.stateName).toEqual('page1');
expect(afterEnter.stateName).toEqual('page1');
expect(enter.stateName).toEqual('page1');
expect(enter.fromCache).toEqual(true);
expect(enter.transitionId).toEqual(3);
}));
it('should emit $ionicView leave events', inject(function ($state, $q, $timeout, $compile) {
var beforeLeave, afterLeave, leave;
scope.$on('$ionicView.beforeLeave', function(ev, d){
beforeLeave = d;
});
scope.$on('$ionicView.afterLeave', function(ev, d){
afterLeave = d;
});
scope.$on('$ionicView.leave', function(ev, d){
leave = d;
});
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
$state.go(page1State);
$q.flush();
$timeout.flush();
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(beforeLeave.stateName).toEqual('page1');
expect(afterLeave.stateName).toEqual('page1');
expect(leave.stateName).toEqual('page1');
expect(leave.transitionId).toEqual(2);
$state.go(page1State);
$q.flush();
$timeout.flush();
expect(beforeLeave.stateName).toEqual('page2');
expect(afterLeave.stateName).toEqual('page2');
expect(leave.stateName).toEqual('page2');
expect(leave.transitionId).toEqual(3);
$state.go(page2State);
$q.flush();
$timeout.flush();
expect(beforeLeave.stateName).toEqual('page1');
expect(afterLeave.stateName).toEqual('page1');
expect(leave.stateName).toEqual('page1');
expect(leave.transitionId).toEqual(4);
}));
it('should emit tab leaving events', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
var beforeLeave, afterLeave, leave;
scope.$on('$ionicView.beforeLeave', function(ev, d){
beforeLeave = d;
});
scope.$on('$ionicView.afterLeave', function(ev, d){
afterLeave = d;
});
scope.$on('$ionicView.leave', function(ev, d){
leave = d;
});
elem.append($compile('<ion-nav-view name="root"></ion-nav-view>')(scope));
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
$state.go(tab1page2State);
$q.flush();
$timeout.flush();
expect(beforeLeave.stateName).toEqual('tabAbstract.tab1page1');
expect(afterLeave.stateName).toEqual('tabAbstract.tab1page1');
expect(leave.stateName).toEqual('tabAbstract.tab1page1');
$state.go(tab2page1State);
$q.flush();
$timeout.flush();
expect(beforeLeave.stateName).toEqual('tabAbstract.tab1page2');
expect(afterLeave.stateName).toEqual('tabAbstract.tab1page2');
expect(leave.stateName).toEqual('tabAbstract.tab1page2');
$state.go(tab3page1State);
$q.flush();
$timeout.flush();
expect(beforeLeave.stateName).toEqual('tabAbstract.tab2page1');
expect(afterLeave.stateName).toEqual('tabAbstract.tab2page1');
expect(leave.stateName).toEqual('tabAbstract.tab2page1');
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
expect(beforeLeave.stateName).toEqual('tabAbstract.tab3page1');
expect(afterLeave.stateName).toEqual('tabAbstract.tab3page1');
expect(leave.stateName).toEqual('tabAbstract.tab3page1');
}));
it('should emit tab $ionicView.unloaded event', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
$ionicConfig.views.maxCache(0);
var unloadedEvent;
scope.$on('$ionicView.unloaded', function(ev, d){
unloadedEvent = d;
});
elem.append($compile('<ion-nav-view name="root"></ion-nav-view>')(scope));
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
$state.go(tab1page2State);
$q.flush();
$timeout.flush();
expect(unloadedEvent.stateName).toEqual('tabAbstract.tab1page1');
$state.go(tab2page1State);
$q.flush();
$timeout.flush();
expect(unloadedEvent.stateName).toEqual('tabAbstract.tab1page2');
$state.go(tab3page1State);
$q.flush();
$timeout.flush();
expect(unloadedEvent.stateName).toEqual('tabAbstract.tab2page1');
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
expect(unloadedEvent.stateName).toEqual('tabAbstract.tab3page1');
}));
it('should clear ion-nav-view cache', inject(function ($state, $q, $timeout, $compile, $ionicHistory) {
elem.append($compile('<div><ion-nav-view></ion-nav-view></div>')(scope));
var clearCacheCollection = [];
scope.$on('$ionicView.unloaded', function(ev, d){
clearCacheCollection.push(d);
});
$state.go(page1State);
$q.flush();
$timeout.flush();
$state.go(page2State);
$q.flush();
$timeout.flush();
$state.go(page3State);
$q.flush();
$timeout.flush();
var divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(3);
expect(divs.eq(0).attr('nav-view')).toBe('cached');
expect(divs.eq(0).text()).toBe('page1');
expect(divs.eq(1).attr('nav-view')).toBe('cached');
expect(divs.eq(1).text()).toBe('page2');
expect(divs.eq(2).attr('nav-view')).toBe('active');
expect(divs.eq(2).text()).toBe('page3');
expect(clearCacheCollection.length).toBe(0);
$ionicHistory.clearCache();
expect(clearCacheCollection.length).toBe(2);
expect(clearCacheCollection[0].stateName).toBe('page1');
expect(clearCacheCollection[1].stateName).toBe('page2');
clearCacheCollection = [];
var divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(1);
expect(divs.eq(0).attr('nav-view')).toBe('active');
expect(divs.eq(0).text()).toBe('page3');
$ionicHistory.clearCache();
expect(clearCacheCollection.length).toBe(0);
var divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(1);
expect(divs.eq(0).attr('nav-view')).toBe('active');
expect(divs.eq(0).text()).toBe('page3');
$state.go(page2State);
$q.flush();
$timeout.flush();
var divs = elem.find('ion-nav-view').find('div');
expect(divs.length).toBe(1);
expect(divs.eq(0).attr('nav-view')).toBe('active');
expect(divs.eq(0).text()).toBe('page2');
expect(clearCacheCollection[0].stateName).toBe('page3');
}));
it('should create and cache tabs', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<ion-nav-view name="root"></ion-nav-view>')(scope));
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
var tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('active');
$state.go(tab2page1State);
$q.flush();
$timeout.flush();
var tab2Ele = elem[0].querySelector('ion-nav-view[name="tab2"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('cached');
expect(tab2Ele.getAttribute('nav-view')).toBe('active');
$state.go(tab3page1State);
$q.flush();
$timeout.flush();
var tab3Ele = elem[0].querySelector('ion-nav-view[name="tab3"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('cached');
expect(tab2Ele.getAttribute('nav-view')).toBe('cached');
expect(tab3Ele.getAttribute('nav-view')).toBe('active');
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
expect(tab1Ele.getAttribute('nav-view')).toBe('active');
expect(tab2Ele.getAttribute('nav-view')).toBe('cached');
expect(tab3Ele.getAttribute('nav-view')).toBe('cached');
}));
it('should clear cached tab and reload on return to tab', inject(function ($state, $q, $timeout, $compile, $ionicHistory) {
elem.append($compile('<ion-nav-view name="root"></ion-nav-view>')(scope));
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
var tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('active');
expect(tab1Ele.childElementCount).toBe(1);
$state.go(tab1page2State);
$q.flush();
$timeout.flush();
tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('active');
expect(tab1Ele.childElementCount).toBe(2);
$state.go(tab2page1State);
$q.flush();
$timeout.flush();
var tab2Ele = elem[0].querySelector('ion-nav-view[name="tab2"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('cached');
expect(tab1Ele.childElementCount).toBe(2);
expect(tab2Ele.getAttribute('nav-view')).toBe('active');
expect(tab2Ele.childElementCount).toBe(1);
$ionicHistory.clearCache();
tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab1Ele).toEqual(null);
tab2Ele = elem[0].querySelector('ion-nav-view[name="tab2"]');
expect(tab2Ele.getAttribute('nav-view')).toBe('active');
expect(tab2Ele.childElementCount).toBe(1);
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('active');
expect(tab1Ele.childElementCount).toBe(1);
}));
it('should not cache ion-views when going between history and its the first load, stateParam cache=false', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<ion-nav-view name="root"></ion-nav-view>')(scope));
$state.go(rootView1NoCacheState);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('ion-view').length).toBe(1);
expect(elem.find('ion-nav-view').find('ion-view').eq(0).text()).toBe('rootView1NoCacheState');
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
var tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('active');
expect(elem[0].querySelector('ion-nav-view[name="root"]').children.length).toBe(1);
$state.go(tab2page1State);
$q.flush();
$timeout.flush();
var tab2Ele = elem[0].querySelector('ion-nav-view[name="tab2"]');
expect(tab2Ele.getAttribute('nav-view')).toBe('active');
tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('cached');
}));
it('should not cache ion-views when going between history and its the first load, maxCache(0)', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<ion-nav-view name="root"></ion-nav-view>')(scope));
$ionicConfig.views.maxCache(0);
$state.go(rootView1State);
$q.flush();
$timeout.flush();
expect(elem.find('ion-nav-view').find('ion-view').length).toBe(1);
expect(elem.find('ion-nav-view').find('ion-view').eq(0).text()).toBe('rootView1State');
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
var tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab1Ele.getAttribute('nav-view')).toBe('active');
expect(elem[0].querySelector('ion-nav-view[name="root"]').children.length).toBe(1);
$state.go(tab2page1State);
$q.flush();
$timeout.flush();
var tab2Ele = elem[0].querySelector('ion-nav-view[name="tab2"]');
expect(tab2Ele.getAttribute('nav-view')).toBe('active');
tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab1Ele).toBe(null);
}));
it('should not cache any views when going root1 to tabs to root 2', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<ion-nav-view name="root"></ion-nav-view>')(scope));
$ionicConfig.views.maxCache(0);
$state.go(rootView1State);
$q.flush();
$timeout.flush();
expect(elem[0].querySelector('ion-nav-view[name="root"]').children.length).toBe(1);
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
expect(elem[0].querySelector('ion-nav-view[name="root"]').children.length).toBe(1);
$state.go(rootView2State);
$q.flush();
$timeout.flush();
expect(elem[0].querySelector('ion-nav-view[name="root"]').children.length).toBe(1);
$state.go(rootView1State);
$q.flush();
$timeout.flush();
expect(elem[0].querySelector('ion-nav-view[name="root"]').children.length).toBe(1);
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
expect(elem[0].querySelector('ion-nav-view[name="root"]').children.length).toBe(1);
$state.go(rootView2State);
$q.flush();
$timeout.flush();
expect(elem[0].querySelector('ion-nav-view[name="root"]').children.length).toBe(1);
}));
it('should not cache a tab with cache false state property', inject(function ($state, $q, $timeout, $compile, $ionicConfig) {
elem.append($compile('<ion-nav-view name="root"></ion-nav-view>')(scope));
$state.go(tab3page2NoCacheState);
$q.flush();
$timeout.flush();
var tab3NoCacheEle = elem[0].querySelector('ion-nav-view[name="tab3"]');
expect(tab3NoCacheEle.getAttribute('nav-view')).toBe('active');
var tab3InnerEle = tab3NoCacheEle.querySelector('[nav-view="active"]');
expect(tab3InnerEle.innerText).toEqual('tab3page2NoCache');
$state.go(tab1page1State);
$q.flush();
$timeout.flush();
var tab1Ele = elem[0].querySelector('ion-nav-view[name="tab1"]');
expect(tab3NoCacheEle.getAttribute('nav-view')).toBe('cached');
expect(tab1Ele.getAttribute('nav-view')).toBe('active');
expect(tab1Ele.innerText).toEqual('tab1page1');
tab3InnerEle = tab3NoCacheEle.querySelector('[nav-view="active"]');
expect(tab3InnerEle).toBe(null);
}));
});
angular.module('ngMock').config(function ($provide) {
$provide.decorator('$q', function ($delegate, $rootScope) {
$delegate.flush = function() {
$rootScope.$digest();
};
// Add callbacks to the promise that expose the resolved value/error
function expose(promise) {
// Don't add hooks to the same promise twice (shouldn't happen anyway)
if (!promise.hasOwnProperty('$$resolved')) {
promise.$$resolved = false;
promise.then(function (value) {
promise.$$resolved = { success: true, value: value };
}, function (error) {
promise.$$resolved = { success: false, error: error };
});
// We need to expose() any then()ed promises recursively
var qThen = promise.then;
promise.then = function () {
return expose(qThen.apply(this, arguments));
};
}
return promise;
}
// Wrap functions that return a promise
angular.forEach([ 'when', 'all', 'reject'], function (name) {
var qFunc = $delegate[name];
$delegate[name] = function () {
return expose(qFunc.apply(this, arguments));
};
});
// Wrap defer()
var qDefer = $delegate.defer;
$delegate.defer = function () {
var deferred = qDefer();
expose(deferred.promise);
return deferred;
};
return $delegate;
});
});