Update the license headers throughout the repository to reference Google LLC rather than Google Inc, for the required license headers. PR Close #37205
		
			
				
	
	
		
			189 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			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]);
 | |
|   }
 | |
| });
 |