mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Instead of using pointer-events: none to disable unwanted clicks which can cause flickering, we’re now using a click-block div that covers the view during transitions. Similar concept to pointer-events: none applied to the body tag, but in tests its showing to be more effective to not cause any flickers.
406 lines
14 KiB
JavaScript
406 lines
14 KiB
JavaScript
describe('$ionicScroll Controller', function() {
|
|
|
|
beforeEach(module('ionic'));
|
|
|
|
beforeEach(function() {
|
|
ionic.Platform.ready = function(cb) { cb(); };
|
|
});
|
|
|
|
var scope, ctrl, timeout;
|
|
function setup(options) {
|
|
options = options || {};
|
|
|
|
options.el = options.el ||
|
|
//scrollView requires an outer container element and a child
|
|
//content element
|
|
angular.element('<div><div></div></div>')[0];
|
|
angular.element('<div>').append(options.el);
|
|
|
|
inject(function($controller, $rootScope, $timeout) {
|
|
scope = $rootScope.$new();
|
|
ctrl = $controller('$ionicScroll', {
|
|
$scope: scope,
|
|
scrollViewOptions: options
|
|
});
|
|
spyOn(ctrl.scrollView, 'run'); // don't actually call run, this is a dumb test
|
|
timeout = $timeout;
|
|
});
|
|
}
|
|
|
|
it('should register with no handle', inject(function($ionicScrollDelegate) {
|
|
spyOn($ionicScrollDelegate, '_registerInstance');
|
|
var el = setup();
|
|
expect($ionicScrollDelegate._registerInstance)
|
|
.toHaveBeenCalledWith(ctrl, undefined);
|
|
}));
|
|
|
|
it('should register with given handle and deregister on destroy', inject(function($ionicScrollDelegate) {
|
|
var deregisterSpy = jasmine.createSpy('deregister');
|
|
spyOn($ionicScrollDelegate, '_registerInstance').andCallFake(function() {
|
|
return deregisterSpy;
|
|
});
|
|
setup({
|
|
delegateHandle: 'something'
|
|
});
|
|
expect($ionicScrollDelegate._registerInstance)
|
|
.toHaveBeenCalledWith(ctrl, 'something');
|
|
|
|
expect(deregisterSpy).not.toHaveBeenCalled();
|
|
scope.$destroy();
|
|
expect(deregisterSpy).toHaveBeenCalled();
|
|
}));
|
|
|
|
it('should set bouncing to option if given', function() {
|
|
spyOn(ionic.Platform, 'isAndroid');
|
|
setup({
|
|
bouncing: true
|
|
});
|
|
expect(ionic.Platform.isAndroid).not.toHaveBeenCalled();
|
|
});
|
|
it('should set bouncing false if isAndroid true', function() {
|
|
spyOn(ionic.Platform, 'isAndroid').andCallFake(function() {
|
|
return true;
|
|
});
|
|
setup();
|
|
expect(ctrl.scrollView.options.bouncing).toBe(false);
|
|
});
|
|
it('should set bouncing true if android false', function() {
|
|
spyOn(ionic.Platform, 'isAndroid').andCallFake(function() {
|
|
return false;
|
|
});
|
|
setup();
|
|
expect(ctrl.scrollView.options.bouncing).toBe(true);
|
|
});
|
|
|
|
it('should set this.element and this.$element', function() {
|
|
setup();
|
|
expect(ctrl.element.tagName).toMatch(/div/i);
|
|
expect(ctrl.$element[0]).toBe(ctrl.element);
|
|
});
|
|
|
|
it('should register scrollView as this.scrollView', function() {
|
|
setup();
|
|
expect(ctrl.scrollView instanceof ionic.views.Scroll).toBe(true);
|
|
});
|
|
|
|
it('should register controller on element.data', function() {
|
|
setup();
|
|
expect(ctrl.$element.parent().data('$$ionicScrollController')).toBe(ctrl);
|
|
});
|
|
|
|
it('should run after a timeout', function() {
|
|
setup();
|
|
timeout.flush();
|
|
expect(ctrl.scrollView.run).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should resize the scrollview on window resize', function() {
|
|
setup();
|
|
timeout.flush();
|
|
spyOn(ctrl.scrollView, 'resize');
|
|
ionic.trigger('resize', { target: window });
|
|
expect(ctrl.scrollView.resize).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should not remember scroll position on $viewContentLoaded if no viewId', function() {
|
|
var historyData = {};
|
|
setup();
|
|
spyOn(ctrl, 'rememberScrollPosition');
|
|
spyOn(ctrl, 'scrollToRememberedPosition');
|
|
scope.$broadcast('$viewContentLoaded', historyData);
|
|
timeout.flush();
|
|
expect(ctrl.rememberScrollPosition).not.toHaveBeenCalledWith();
|
|
expect(ctrl.scrollToRememberedPosition).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should remember scroll position on $viewContentLoaded event', function() {
|
|
var historyData = { viewId: '1' };
|
|
setup();
|
|
spyOn(ctrl, 'rememberScrollPosition');
|
|
spyOn(ctrl, 'scrollToRememberedPosition');
|
|
scope.$broadcast('$viewContentLoaded', historyData);
|
|
timeout.flush();
|
|
expect(ctrl.rememberScrollPosition).toHaveBeenCalledWith('1');
|
|
expect(ctrl.scrollToRememberedPosition).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should forget on $viewHistory.viewBack after $viewContentLoaded', inject(function($rootScope, $timeout) {
|
|
var historyData = { viewId: 'foo' };
|
|
setup();
|
|
spyOn($rootScope, '$on').andCallThrough();
|
|
scope.$broadcast('$viewContentLoaded', historyData);
|
|
$timeout.flush();
|
|
expect(scope.$on).toHaveBeenCalledWith('$viewHistory.viewBack', jasmine.any(Function));
|
|
|
|
//Should not forget unless backViewId is the same
|
|
spyOn(ctrl, 'forgetScrollPosition');
|
|
$rootScope.$broadcast('$viewHistory.viewBack', 'bar');
|
|
expect(ctrl.forgetScrollPosition).not.toHaveBeenCalled();
|
|
$rootScope.$broadcast('$viewHistory.viewBack', 'foo');
|
|
expect(ctrl.forgetScrollPosition).toHaveBeenCalled();
|
|
}));
|
|
|
|
it('should not remember scrollValues on $destroy without id', inject(function($$scrollValueCache) {
|
|
setup();
|
|
spyOn(ctrl.scrollView, 'getValues');
|
|
scope.$destroy();
|
|
expect(ctrl.scrollView.getValues).not.toHaveBeenCalled();
|
|
expect($$scrollValueCache).toEqual({});
|
|
}));
|
|
|
|
it('should remember scrollValues on $destroy with id', inject(function($$scrollValueCache) {
|
|
setup();
|
|
ctrl.rememberScrollPosition('super');
|
|
spyOn(ctrl.scrollView, 'getValues').andCallFake(function() {
|
|
return 'scrollValues';
|
|
});
|
|
scope.$destroy();
|
|
expect(ctrl.scrollView.getValues).toHaveBeenCalled();
|
|
expect($$scrollValueCache).toEqual({
|
|
'super': 'scrollValues'
|
|
});
|
|
}));
|
|
|
|
it('rememberScrollPosition should throw without id', function() {
|
|
setup();
|
|
expect(function() {
|
|
ctrl.rememberScrollPosition();
|
|
}).toThrow();
|
|
});
|
|
|
|
it('should unbind window event listener on scope destroy', inject(function($window) {
|
|
spyOn(ionic, 'on');
|
|
spyOn(ionic, 'off');
|
|
setup();
|
|
expect(ionic.on).toHaveBeenCalledWith('resize', jasmine.any(Function), $window);
|
|
scope.$destroy();
|
|
expect(ionic.off).toHaveBeenCalledWith('resize', jasmine.any(Function), $window);
|
|
}));
|
|
|
|
it('rememberScrollPosition should set id', function() {
|
|
setup();
|
|
expect(ctrl._rememberScrollId).toBeFalsy();
|
|
ctrl.rememberScrollPosition('banana');
|
|
expect(ctrl._rememberScrollId).toBe('banana');
|
|
});
|
|
|
|
it('forgetScrollPosition should remove from cache and unset id', inject(function($$scrollValueCache) {
|
|
setup();
|
|
ctrl._rememberScrollId = 'elephant';
|
|
$$scrollValueCache.elephant = 'stampede';
|
|
ctrl.forgetScrollPosition();
|
|
expect(ctrl._rememberScrollId).toBeFalsy();
|
|
expect($$scrollValueCache.elephant).toBeFalsy();
|
|
}));
|
|
|
|
it('should listen to scroll event and call $onScroll', function() {
|
|
setup();
|
|
scope.$onScroll = jasmine.createSpy();
|
|
|
|
expect(scope.$onScroll).not.toHaveBeenCalled();
|
|
|
|
ionic.trigger('scroll', {target: ctrl.element});
|
|
expect(scope.$onScroll).toHaveBeenCalled();
|
|
expect(scope.$onScroll.mostRecentCall.args[0].scrollLeft).toBe(0);
|
|
expect(scope.$onScroll.mostRecentCall.args[0].scrollTop).toBe(0);
|
|
|
|
scope.$onScroll.reset();
|
|
ionic.trigger('scroll', {target: ctrl.element, scrollTop: 3, scrollLeft: 4});
|
|
expect(scope.$onScroll.mostRecentCall.args[0].scrollLeft).toBe(4);
|
|
expect(scope.$onScroll.mostRecentCall.args[0].scrollTop).toBe(3);
|
|
});
|
|
|
|
it('.resize() should resize after timeout', inject(function($timeout) {
|
|
setup();
|
|
$timeout.flush();
|
|
spyOn(ctrl.scrollView, 'resize');
|
|
ctrl.resize();
|
|
expect(ctrl.scrollView.resize).not.toHaveBeenCalled();
|
|
$timeout.flush();
|
|
expect(ctrl.scrollView.resize).toHaveBeenCalled();
|
|
}));
|
|
|
|
it('.getScrollView', function() {
|
|
setup();
|
|
expect(ctrl.getScrollView()).toBe(ctrl.scrollView);
|
|
});
|
|
it('.getScrollPosition', function() {
|
|
setup();
|
|
var values = {};
|
|
spyOn(ctrl.scrollView, 'getValues').andReturn(values);
|
|
expect(ctrl.getScrollPosition()).toBe(values);
|
|
});
|
|
|
|
[false, true].forEach(function(shouldAnimate) {
|
|
describe('with animate='+shouldAnimate, function() {
|
|
|
|
describe('scroll action', function() {
|
|
beforeEach(function() {
|
|
setup();
|
|
//Mock resize to insta-call through for easier tests
|
|
ctrl.resize = function() {
|
|
return { then: function(cb) { cb(); } };
|
|
};
|
|
});
|
|
|
|
it('scrollToRememberedPosition should work', inject(function($$scrollValueCache) {
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
$$scrollValueCache.foo = { left: 3, top: 4 };
|
|
ctrl._rememberScrollId = 'foo';
|
|
expect(ctrl.scrollView.scrollTo).not.toHaveBeenCalled();
|
|
ctrl.scrollToRememberedPosition(shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(3, 4, shouldAnimate);
|
|
}));
|
|
it('.scrollTop', function() {
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.scrollTop(shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(0, 0, shouldAnimate);
|
|
});
|
|
it('.scrollBottom', function() {
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
spyOn(ctrl.scrollView, 'getScrollMax').andCallFake(function() {
|
|
return { left: 33, top: 44 };
|
|
});
|
|
ctrl.scrollBottom(shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(33, 44, shouldAnimate);
|
|
});
|
|
it('.scrollTo', function() {
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.scrollTo(1, 2, shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(1, 2, shouldAnimate);
|
|
});
|
|
it('.scrollBy', function() {
|
|
spyOn(ctrl.scrollView, 'scrollBy');
|
|
ctrl.scrollBy(1, 2, shouldAnimate);
|
|
expect(ctrl.scrollView.scrollBy).toHaveBeenCalledWith(1, 2, shouldAnimate);
|
|
});
|
|
it('.anchorScroll without hash should scrollTop', inject(function($location, $document) {
|
|
$document[0].getElementById = jasmine.createSpy();
|
|
$location.hash = function() { return null; };
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.anchorScroll(shouldAnimate);
|
|
expect($document[0].getElementById).not.toHaveBeenCalled();
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(0, 0, shouldAnimate);
|
|
}));
|
|
it('.anchorScroll with hash but no element should scrollTop', inject(function($location, $document) {
|
|
$document[0].getElementById = jasmine.createSpy();
|
|
$location.hash = function() { return 'foo'; };
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.anchorScroll(shouldAnimate);
|
|
expect($document[0].getElementById).toHaveBeenCalledWith('foo');
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(0, 0, shouldAnimate);
|
|
}));
|
|
it('.anchorScroll with el matching hash should scroll to it', inject(function($location, $document) {
|
|
$document[0].getElementById = jasmine.createSpy('byId').andCallFake(function() {
|
|
return {
|
|
offsetLeft: 8,
|
|
offsetTop: 9,
|
|
attributes:[],
|
|
offsetParent:{}
|
|
};
|
|
});
|
|
spyOn($location, 'hash').andCallFake(function() {
|
|
return 'foo';
|
|
});
|
|
spyOn(ctrl.scrollView, 'scrollTo');
|
|
ctrl.anchorScroll(shouldAnimate);
|
|
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(8, 9, shouldAnimate);
|
|
}));
|
|
});
|
|
});
|
|
});
|
|
|
|
it('.anchorScroll with el matching hash should scroll to it, even if the el is not directly below the list', function() {
|
|
var ele = {
|
|
offsetLeft: 8,
|
|
offsetTop: 9,
|
|
attributes:[],
|
|
offsetParent:{
|
|
offsetLeft: 10,
|
|
offsetTop: 11,
|
|
attributes:[],
|
|
offsetParent:{}
|
|
}
|
|
};
|
|
module('ionic', function($provide) {
|
|
$provide.value('$document', [ { getElementById: function(){ return ele; }, createElement: function(tagName){ return document.createElement(tagName); } } ]);
|
|
});
|
|
inject(function($controller, $rootScope, $location, $timeout) {
|
|
var scrollCtrl = $controller('$ionicScroll', {
|
|
$scope: $rootScope.$new(),
|
|
$element: jqLite('<div><div></div></div>'),
|
|
scrollViewOptions: { el: jqLite('<div><div></div></div>')[0] }
|
|
});
|
|
spyOn($location, 'hash').andCallFake(function() {
|
|
return 'bar';
|
|
});
|
|
spyOn(scrollCtrl.scrollView, 'scrollTo')
|
|
scrollCtrl.anchorScroll()
|
|
$timeout.flush();
|
|
expect(scrollCtrl.scrollView.scrollTo.mostRecentCall.args).toEqual([18, 20, false]);
|
|
});
|
|
});
|
|
|
|
it('should not activatePullToRefresh if setRefresher is not called', function() {
|
|
setup();
|
|
timeout.flush();
|
|
expect(ctrl.refresher).toBeFalsy();
|
|
spyOn(ctrl.scrollView, 'activatePullToRefresh');
|
|
expect(ctrl.scrollView.activatePullToRefresh).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should activatePullToRefresh and work when setRefresher', function() {
|
|
var startCb, refreshingCb, doneCb, refresherEl;
|
|
setup({
|
|
el: angular.element('<div><div class="scroll-refresher"></div></div>')[0]
|
|
});
|
|
spyOn(ctrl.scrollView, 'activatePullToRefresh').andCallFake(function(height, start, refreshing, done, show, hide) {
|
|
startCb = start;
|
|
refreshingCb = refreshing;
|
|
doneCb = done;
|
|
showCb = show;
|
|
hideCb = hide;
|
|
});
|
|
ctrl._setRefresher(scope, ctrl.element);
|
|
|
|
var scrollOnRefreshSpy = jasmine.createSpy('scroll.onRefresh');
|
|
|
|
scope.$onRefresh = jasmine.createSpy('onRefresh');
|
|
scope.$onPulling = jasmine.createSpy('onPulling');
|
|
|
|
timeout.flush();
|
|
var refresher = ctrl.refresher;
|
|
|
|
expect(refresher.classList.contains('active')).toBe(false);
|
|
expect(refresher.classList.contains('refreshing')).toBe(false);
|
|
|
|
startCb();
|
|
expect(refresher.classList.contains('active')).toBe(true);
|
|
expect(refresher.classList.contains('refreshing')).toBe(false);
|
|
expect(scope.$onPulling).toHaveBeenCalled();
|
|
|
|
refreshingCb();
|
|
expect(refresher.classList.contains('active')).toBe(true);
|
|
expect(refresher.classList.contains('refreshing')).toBe(false);
|
|
|
|
expect(scope.$onRefresh).not.toHaveBeenCalled();
|
|
|
|
doneCb();
|
|
expect(refresher.classList.contains('active')).toBe(true);
|
|
expect(refresher.classList.contains('refreshing')).toBe(true);
|
|
expect(scope.$onRefresh).toHaveBeenCalled();
|
|
timeout.flush();
|
|
|
|
expect(refresher.classList.contains('active')).toBe(false);
|
|
expect(refresher.classList.contains('refreshing')).toBe(false);
|
|
expect(refresher.classList.contains('invisible')).toBe(true);
|
|
|
|
showCb();
|
|
expect(refresher.classList.contains('invisible')).toBe(false);
|
|
|
|
hideCb();
|
|
expect(refresher.classList.contains('invisible')).toBe(true);
|
|
});
|
|
|
|
});
|