angular-cn/packages/router/test/router_scroller.spec.ts
Joey Perrott d1ea1f4c7f build: update license headers to reference Google LLC ()
Update the license headers throughout the repository to reference Google LLC
rather than Google Inc, for the required license headers.

PR Close 
2020-05-26 14:26:58 -04:00

189 lines
7.6 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {fakeAsync, tick} from '@angular/core/testing';
import {DefaultUrlSerializer, NavigationEnd, NavigationStart, RouterEvent} from '@angular/router';
import {Subject} from 'rxjs';
import {filter, switchMap} from 'rxjs/operators';
import {Scroll} from '../src/events';
import {RouterScroller} from '../src/router_scroller';
describe('RouterScroller', () => {
it('defaults to disabled', () => {
const events = new Subject<RouterEvent>();
const router = <any>{
events,
parseUrl: (url: any) => new DefaultUrlSerializer().parse(url),
triggerEvent: (e: any) => events.next(e)
};
const viewportScroller = jasmine.createSpyObj(
'viewportScroller',
['getScrollPosition', 'scrollToPosition', 'scrollToAnchor', 'setHistoryScrollRestoration']);
setScroll(viewportScroller, 0, 0);
const scroller = new RouterScroller(router, router);
expect((scroller as any).options.scrollPositionRestoration).toBe('disabled');
expect((scroller as any).options.anchorScrolling).toBe('disabled');
});
describe('scroll to top', () => {
it('should scroll to the top', () => {
const {events, viewportScroller} =
createRouterScroller({scrollPositionRestoration: 'top', anchorScrolling: 'disabled'});
events.next(new NavigationStart(1, '/a'));
events.next(new NavigationEnd(1, '/a', '/a'));
expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([0, 0]);
events.next(new NavigationStart(2, '/a'));
events.next(new NavigationEnd(2, '/b', '/b'));
expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([0, 0]);
events.next(new NavigationStart(3, '/a', 'popstate'));
events.next(new NavigationEnd(3, '/a', '/a'));
expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([0, 0]);
});
});
describe('scroll to the stored position', () => {
it('should scroll to the stored position on popstate', () => {
const {events, viewportScroller} =
createRouterScroller({scrollPositionRestoration: 'enabled', anchorScrolling: 'disabled'});
events.next(new NavigationStart(1, '/a'));
events.next(new NavigationEnd(1, '/a', '/a'));
setScroll(viewportScroller, 10, 100);
expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([0, 0]);
events.next(new NavigationStart(2, '/b'));
events.next(new NavigationEnd(2, '/b', '/b'));
setScroll(viewportScroller, 20, 200);
expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([0, 0]);
events.next(new NavigationStart(3, '/a', 'popstate', {navigationId: 1}));
events.next(new NavigationEnd(3, '/a', '/a'));
expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([10, 100]);
});
});
describe('anchor scrolling', () => {
it('should work (scrollPositionRestoration is disabled)', () => {
const {events, viewportScroller} =
createRouterScroller({scrollPositionRestoration: 'disabled', anchorScrolling: 'enabled'});
events.next(new NavigationStart(1, '/a#anchor'));
events.next(new NavigationEnd(1, '/a#anchor', '/a#anchor'));
expect(viewportScroller.scrollToAnchor).toHaveBeenCalledWith('anchor');
events.next(new NavigationStart(2, '/a#anchor2'));
events.next(new NavigationEnd(2, '/a#anchor2', '/a#anchor2'));
expect(viewportScroller.scrollToAnchor).toHaveBeenCalledWith('anchor2');
viewportScroller.scrollToAnchor.calls.reset();
// we never scroll to anchor when navigating back.
events.next(new NavigationStart(3, '/a#anchor', 'popstate'));
events.next(new NavigationEnd(3, '/a#anchor', '/a#anchor'));
expect(viewportScroller.scrollToAnchor).not.toHaveBeenCalled();
expect(viewportScroller.scrollToPosition).not.toHaveBeenCalled();
});
it('should work (scrollPositionRestoration is enabled)', () => {
const {events, viewportScroller} =
createRouterScroller({scrollPositionRestoration: 'enabled', anchorScrolling: 'enabled'});
events.next(new NavigationStart(1, '/a#anchor'));
events.next(new NavigationEnd(1, '/a#anchor', '/a#anchor'));
expect(viewportScroller.scrollToAnchor).toHaveBeenCalledWith('anchor');
events.next(new NavigationStart(2, '/a#anchor2'));
events.next(new NavigationEnd(2, '/a#anchor2', '/a#anchor2'));
expect(viewportScroller.scrollToAnchor).toHaveBeenCalledWith('anchor2');
viewportScroller.scrollToAnchor.calls.reset();
// we never scroll to anchor when navigating back
events.next(new NavigationStart(3, '/a#anchor', 'popstate', {navigationId: 1}));
events.next(new NavigationEnd(3, '/a#anchor', '/a#anchor'));
expect(viewportScroller.scrollToAnchor).not.toHaveBeenCalled();
expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([0, 0]);
});
});
describe('extending a scroll service', () => {
it('work', fakeAsync(() => {
const {events, viewportScroller, router} = createRouterScroller(
{scrollPositionRestoration: 'disabled', anchorScrolling: 'disabled'});
router.events
.pipe(filter(e => e instanceof Scroll && !!e.position), switchMap(p => {
// can be any delay (e.g., we can wait for NgRx store to emit an event)
const r = new Subject<any>();
setTimeout(() => {
r.next(p);
r.complete();
}, 1000);
return r;
}))
.subscribe((e: Scroll) => {
viewportScroller.scrollToPosition(e.position);
});
events.next(new NavigationStart(1, '/a'));
events.next(new NavigationEnd(1, '/a', '/a'));
setScroll(viewportScroller, 10, 100);
events.next(new NavigationStart(2, '/b'));
events.next(new NavigationEnd(2, '/b', '/b'));
setScroll(viewportScroller, 20, 200);
events.next(new NavigationStart(3, '/c'));
events.next(new NavigationEnd(3, '/c', '/c'));
setScroll(viewportScroller, 30, 300);
events.next(new NavigationStart(4, '/a', 'popstate', {navigationId: 1}));
events.next(new NavigationEnd(4, '/a', '/a'));
tick(500);
expect(viewportScroller.scrollToPosition).not.toHaveBeenCalled();
events.next(new NavigationStart(5, '/a', 'popstate', {navigationId: 1}));
events.next(new NavigationEnd(5, '/a', '/a'));
tick(5000);
expect(viewportScroller.scrollToPosition).toHaveBeenCalledWith([10, 100]);
}));
});
function createRouterScroller({scrollPositionRestoration, anchorScrolling}: {
scrollPositionRestoration: 'disabled'|'enabled'|'top',
anchorScrolling: 'disabled'|'enabled'
}) {
const events = new Subject<RouterEvent>();
const router = <any>{
events,
parseUrl: (url: any) => new DefaultUrlSerializer().parse(url),
triggerEvent: (e: any) => events.next(e)
};
const viewportScroller = jasmine.createSpyObj(
'viewportScroller',
['getScrollPosition', 'scrollToPosition', 'scrollToAnchor', 'setHistoryScrollRestoration']);
setScroll(viewportScroller, 0, 0);
const scroller =
new RouterScroller(router, viewportScroller, {scrollPositionRestoration, anchorScrolling});
scroller.init();
return {events, viewportScroller, router};
}
function setScroll(viewportScroller: any, x: number, y: number) {
viewportScroller.getScrollPosition.and.returnValue([x, y]);
}
});