mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
#### Refactor: * **Navigation:** Refactored navigation for improved performance, reduce DOM manipulations, increase transition FPS, cached views, smoother transitions, platform specific transitions with added configurable controls for transition animation and direction. * **Cached Views:** Previously as a user navigated an app, each leaving view’s element and scope would be destroyed. If the same view was accessed again then the app would have to recreate the element. Views can now be cached to improve performance. When a view is navigated away from, its element is left in the DOM, and its scope is disconnected from the cycle. When navigating to a view which is already cached, its scope is reconnected, and the existing element which was left in the DOM becomes the active view. This also allows for scroll position of previous views to be maintained (without skippy jumps). Config variables can be used to disable view caching (set to 0), or change the maximum number of views to cache. * **Angular v1.3:** Upgraded Ionic’s to work with Angular v1.3. In general Ionic just works with the upgrade, but the required change was that animations in v1.3 uses promise, whereas in v1.2 animations used callbacks. #### Features: * **Platform Specific Transitions:** Transitions between views now default to the transition style appropriate for each platform. For example, iOS will move forward by transitioning the entering view from right to center, and the leaving view from center to left. However, Android will transition with the entering view going from bottom to center, covering the previous view, which remains stationary. Platform transitions are automatically applied by default, but config variables and custom CSS allows these defaults to be easily overridden. * **Override Transition Type and Direction:** As a user navigates the app, Ionic automatically applies the appropriate transition type for the platform, and the direction the user is navigating. However, both can be overridden in numerous ways: config variable, view attribute, stateProvider property, or attribute on the button/link that initiated the transition. * **enable-menu-with-back-views:** The `enable-menu-with-back-views` attribute determines if the side menu is enabled when the back button is showing. When set to `false`, any buttons/links with the `menuToggle` directive will be hidden, and the user cannot swipe to open the menu. When going back to the root page of the side menu (the page without a back button visible), then any menuToggle buttons will show again, and menus will be enabled again. * **menuClose:** Closes a side menu which is currently opened. Additionally, the menuClose directive will now cause transitions to not animate between views while the menu is being closed. * **ionNavBackButton:** The back button icon and text will automatically update to platform config defaults, such as adjusting to the platform back icon. To take advantage of this, the `ionNavBackButton` directive now should be empty, such as `<ion-nav-back-button></ion-nav-back-button>`. The back button can still be fully customized like it could before, but without any inner content it knows to style using platform configs. * **navBar button primary/secondary sides:** Primary and secondary sides are now the recommended values for the `side` attribute, such as `<ion-nav-buttons side="primary">`. Primary buttons generally map to the left side of the header, and secondary buttons are generally on the right side. However, their exact locations are platform specific. For example, in iOS the primary buttons are on the far left of the header, and secondary buttons are on the far right, with the header title centered between them. For Android however, both groups of buttons are on the far right of the header, with the header title aligned left. Recommendation is to always use `primary` and `secondary` so buttons correctly map to the side familiar to users of a platform. However, in cases where buttons should always be on an exact side, both `left` and `right` sides are still available. * **navDirection:** An attribute directive that sets the direction which the nav view transition should animate. * **navTransition:** An attribute directive that sets the transition type which the nav view transition should use when it animates. Using `none` will disable an animation. #### Breaking Changes: * **Animation CSS:** The CSS for view transitions have changed. This is a breaking change only if Ionic apps had customized Ionic’s animation CSS. * **$ionicPlatformDefaults:** Platform config variables are no longer in the $ionicPlatformDefaults constant, but within `$ionicConfig`. * **$ionicViewService:** In the navigation refactoring, $ionicViewService was split up into two factories, `$ionicViewSwitcher` and `$ionicHistory`. The `$ionicHistory` is largely what `$ionicViewService`, but between the two factories there is a better separation of concerns for improved testing. * **navClear:** The navClear directive was created to do what the new side menu `enable-menu-with-back-views` attribute accomplishes. Additionally, the new `navTransition` and `navDirection` directives are more useful and granular than the navClear directive. * **scrollView.rememberScrollPosition:** This method has been removed since it is no longer needed with cached views. #### Deprecated: * **ionView.title:** The `ionView` directive used the `title` attribute, but this can cause the tooltip to show up on desktop browsers. The `title` attribute will still work for backwards compatibility, but we now recommend using `view-title`, such as `<ion-view view-title=”My Title”>`. * **ionNavView animation attribute removed:** The animation attribute is no longer used for nav views. Instead use `$ionicConfig`. * **ionNavBar animation attribute removed:** The animation attribute is no longer used for nav bars. Instead use `$ionicConfig`.
730 lines
22 KiB
JavaScript
730 lines
22 KiB
JavaScript
|
|
describe('Ionic nav-view', function() {
|
|
beforeEach(module('ionic'));
|
|
|
|
var compile, viewService, $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
|
|
};
|
|
|
|
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);
|
|
}));
|
|
|
|
beforeEach(inject(function(_$compile_, $ionicHistory, $ionicConfig, $rootScope) {
|
|
viewService = $ionicHistory;
|
|
$compile = _$compile_;
|
|
scope = $rootScope.$new();
|
|
elem = angular.element('<div>');
|
|
|
|
ionic.Platform.setPlatform('ios');
|
|
$ionicConfig.views.transition('none');
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$timeout.flush();
|
|
|
|
$state.go(page2State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
|
|
$state.go(page3State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
|
|
$state.go(page4State);
|
|
$q.flush();
|
|
$timeout.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();
|
|
$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();
|
|
$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();
|
|
$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 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));
|
|
|
|
$ionicConfig.views.maxCache(2);
|
|
|
|
$state.go(page1State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
expect(elem.find('ion-nav-view').find('div').length).toBe(1);
|
|
|
|
$state.go(page2State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
expect(elem.find('ion-nav-view').find('div').length).toBe(2);
|
|
|
|
$state.go(page3State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
expect(elem.find('ion-nav-view').find('div').length).toBe(3);
|
|
|
|
$state.go(page4State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
expect(elem.find('ion-nav-view').find('div').length).toBe(3);
|
|
|
|
$state.go(page3State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
expect(elem.find('ion-nav-view').find('div').length).toBe(2);
|
|
|
|
$state.go(page2State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
expect(elem.find('ion-nav-view').find('div').length).toBe(1);
|
|
|
|
$state.go(page1State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
expect(elem.find('ion-nav-view').find('div').length).toBe(1);
|
|
}));
|
|
|
|
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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$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();
|
|
$timeout.flush();
|
|
divs = elem.find('ion-nav-view').find('div');
|
|
expect(divs.length).toBe(4);
|
|
|
|
$state.go(page1State);
|
|
$q.flush();
|
|
$timeout.flush();
|
|
$timeout.flush();
|
|
divs = elem.find('ion-nav-view').find('div');
|
|
expect(divs.length).toBe(4);
|
|
|
|
$state.go(page5State);
|
|
$q.flush();
|
|
$timeout.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');
|
|
}));
|
|
|
|
});
|
|
|
|
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;
|
|
});
|
|
});
|