diff --git a/packages/router/src/directives/router_link.ts b/packages/router/src/directives/router_link.ts index c12ca2c8cf..a6462ce08e 100644 --- a/packages/router/src/directives/router_link.ts +++ b/packages/router/src/directives/router_link.ts @@ -7,7 +7,7 @@ */ import {LocationStrategy} from '@angular/common'; -import {Attribute, Directive, ElementRef, HostBinding, HostListener, Input, OnChanges, OnDestroy, Renderer2, isDevMode} from '@angular/core'; +import {Attribute, Directive, ElementRef, HostBinding, HostListener, Input, isDevMode, OnChanges, OnDestroy, Renderer2} from '@angular/core'; import {Subscription} from 'rxjs'; import {QueryParamsHandling} from '../config'; @@ -114,21 +114,21 @@ import {UrlTree} from '../url_tree'; @Directive({selector: ':not(a):not(area)[routerLink]'}) export class RouterLink { // TODO(issue/24571): remove '!'. - @Input() queryParams !: {[k: string]: any}; + @Input() queryParams!: {[k: string]: any}; // TODO(issue/24571): remove '!'. - @Input() fragment !: string; + @Input() fragment!: string; // TODO(issue/24571): remove '!'. - @Input() queryParamsHandling !: QueryParamsHandling; + @Input() queryParamsHandling!: QueryParamsHandling; // TODO(issue/24571): remove '!'. - @Input() preserveFragment !: boolean; + @Input() preserveFragment!: boolean; // TODO(issue/24571): remove '!'. - @Input() skipLocationChange !: boolean; + @Input() skipLocationChange!: boolean; // TODO(issue/24571): remove '!'. - @Input() replaceUrl !: boolean; + @Input() replaceUrl!: boolean; @Input() state?: {[k: string]: any}; private commands: any[] = []; // TODO(issue/24571): remove '!'. - private preserve !: boolean; + private preserve!: boolean; constructor( private router: Router, private route: ActivatedRoute, @@ -163,6 +163,7 @@ export class RouterLink { const extras = { skipLocationChange: attrBoolValue(this.skipLocationChange), replaceUrl: attrBoolValue(this.replaceUrl), + state: this.state, }; this.router.navigateByUrl(this.urlTree, extras); return true; @@ -194,28 +195,28 @@ export class RouterLink { @Directive({selector: 'a[routerLink],area[routerLink]'}) export class RouterLinkWithHref implements OnChanges, OnDestroy { // TODO(issue/24571): remove '!'. - @HostBinding('attr.target') @Input() target !: string; + @HostBinding('attr.target') @Input() target!: string; // TODO(issue/24571): remove '!'. - @Input() queryParams !: {[k: string]: any}; + @Input() queryParams!: {[k: string]: any}; // TODO(issue/24571): remove '!'. - @Input() fragment !: string; + @Input() fragment!: string; // TODO(issue/24571): remove '!'. - @Input() queryParamsHandling !: QueryParamsHandling; + @Input() queryParamsHandling!: QueryParamsHandling; // TODO(issue/24571): remove '!'. - @Input() preserveFragment !: boolean; + @Input() preserveFragment!: boolean; // TODO(issue/24571): remove '!'. - @Input() skipLocationChange !: boolean; + @Input() skipLocationChange!: boolean; // TODO(issue/24571): remove '!'. - @Input() replaceUrl !: boolean; + @Input() replaceUrl!: boolean; @Input() state?: {[k: string]: any}; private commands: any[] = []; private subscription: Subscription; // TODO(issue/24571): remove '!'. - private preserve !: boolean; + private preserve!: boolean; // the url displayed on the anchor element. // TODO(issue/24571): remove '!'. - @HostBinding() href !: string; + @HostBinding() href!: string; constructor( private router: Router, private route: ActivatedRoute, @@ -244,8 +245,12 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy { this.preserve = value; } - ngOnChanges(changes: {}): any { this.updateTargetUrlAndHref(); } - ngOnDestroy(): any { this.subscription.unsubscribe(); } + ngOnChanges(changes: {}): any { + this.updateTargetUrlAndHref(); + } + ngOnDestroy(): any { + this.subscription.unsubscribe(); + } @HostListener('click', ['$event.button', '$event.ctrlKey', '$event.metaKey', '$event.shiftKey']) onClick(button: number, ctrlKey: boolean, metaKey: boolean, shiftKey: boolean): boolean { diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 31d9acda5b..6d03da7b1e 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -9,11 +9,11 @@ import {CommonModule, Location} from '@angular/common'; import {SpyLocation} from '@angular/common/testing'; import {ChangeDetectionStrategy, Component, Injectable, NgModule, NgModuleFactoryLoader, NgModuleRef, NgZone, OnDestroy, ɵConsole as Console, ɵNoopNgZone as NoopNgZone} from '@angular/core'; -import {ComponentFixture, TestBed, fakeAsync, inject, tick} from '@angular/core/testing'; +import {ComponentFixture, fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {expect} from '@angular/platform-browser/testing/src/matchers'; -import {ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, CanActivate, CanDeactivate, ChildActivationEnd, ChildActivationStart, DefaultUrlSerializer, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, Navigation, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, PRIMARY_OUTLET, ParamMap, Params, PreloadAllModules, PreloadingStrategy, Resolve, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterEvent, RouterModule, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlSerializer, UrlTree} from '@angular/router'; -import {Observable, Observer, Subscription, of } from 'rxjs'; +import {ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, CanActivate, CanDeactivate, ChildActivationEnd, ChildActivationStart, DefaultUrlSerializer, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, Navigation, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, ParamMap, Params, PreloadAllModules, PreloadingStrategy, PRIMARY_OUTLET, Resolve, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouteReuseStrategy, RouterEvent, RouterModule, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlSerializer, UrlTree} from '@angular/router'; +import {Observable, Observer, of, Subscription} from 'rxjs'; import {filter, first, map, tap} from 'rxjs/operators'; import {forEach} from '../src/utils/collection'; @@ -129,8 +129,8 @@ describe('Integration', () => { router.navigateByUrl('/simple'); tick(); - expect(event !.navigationTrigger).toEqual('imperative'); - expect(event !.restoredState).toEqual(null); + expect(event!.navigationTrigger).toEqual('imperative'); + expect(event!.restoredState).toEqual(null); }))); it('should set history.state if passed using imperative navigation', @@ -141,10 +141,10 @@ describe('Integration', () => { ]); const fixture = createRoot(router, RootCmp); - let navigation: Navigation = null !; + let navigation: Navigation = null!; router.events.subscribe(e => { if (e instanceof NavigationStart) { - navigation = router.getCurrentNavigation() !; + navigation = router.getCurrentNavigation()!; } }); @@ -255,7 +255,9 @@ describe('Integration', () => { let warnings: string[] = []; class MockConsole { - warn(message: string) { warnings.push(message); } + warn(message: string) { + warnings.push(message); + } } beforeEach(() => { @@ -266,7 +268,9 @@ describe('Integration', () => { describe('with NgZone enabled', () => { it('should warn when triggered outside Angular zone', fakeAsync(inject([Router, NgZone], (router: Router, ngZone: NgZone) => { - ngZone.runOutsideAngular(() => { router.navigateByUrl('/simple'); }); + ngZone.runOutsideAngular(() => { + router.navigateByUrl('/simple'); + }); expect(warnings.length).toBe(1); expect(warnings[0]) @@ -276,19 +280,24 @@ describe('Integration', () => { it('should not warn when triggered inside Angular zone', fakeAsync(inject([Router, NgZone], (router: Router, ngZone: NgZone) => { - ngZone.run(() => { router.navigateByUrl('/simple'); }); + ngZone.run(() => { + router.navigateByUrl('/simple'); + }); expect(warnings.length).toBe(0); }))); }); describe('with NgZone disabled', () => { - beforeEach(() => { TestBed.overrideProvider(NgZone, {useValue: new NoopNgZone()}); }); + beforeEach(() => { + TestBed.overrideProvider(NgZone, {useValue: new NoopNgZone()}); + }); it('should not warn when triggered outside Angular zone', fakeAsync(inject([Router, NgZone], (router: Router, ngZone: NgZone) => { - - ngZone.runOutsideAngular(() => { router.navigateByUrl('/simple'); }); + ngZone.runOutsideAngular(() => { + router.navigateByUrl('/simple'); + }); expect(warnings.length).toBe(0); }))); @@ -331,18 +340,24 @@ describe('Integration', () => { @Component({template: ''}) class Parent { constructor(route: ActivatedRoute) { - route.params.subscribe((s: any) => { log.push(s); }); + route.params.subscribe((s: any) => { + log.push(s); + }); } } @Component({template: 'child1'}) class Child1 { - ngOnDestroy() { log.push('child1 destroy'); } + ngOnDestroy() { + log.push('child1 destroy'); + } } @Component({template: 'child2'}) class Child2 { - constructor() { log.push('child2 constructor'); } + constructor() { + log.push('child2 constructor'); + } } @NgModule({ @@ -382,7 +397,6 @@ describe('Integration', () => { 'child2 constructor', ]); }))); - }); it('should not wait for prior navigations to start a new navigation', @@ -412,7 +426,6 @@ describe('Integration', () => { 'trueRightAway', 'trueIn2Seconds-start', 'trueRightAway', 'trueIn2Seconds-start', 'trueIn2Seconds-end', 'trueIn2Seconds-end' ]); - }))); }); @@ -464,11 +477,9 @@ describe('Integration', () => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => { const fixture = createRoot(router, RootCmp); - router.resetConfig([{ - path: 'team/:id', - component: TeamCmp, - children: [{path: 'simple', component: SimpleCmp}] - }]); + router.resetConfig([ + {path: 'team/:id', component: TeamCmp, children: [{path: 'simple', component: SimpleCmp}]} + ]); router.navigateByUrl('/team/33/simple'); advance(fixture); @@ -535,7 +546,9 @@ describe('Integration', () => { @Component({template: `record`}) class RecordLocationCmp { private storedPath: string; - constructor(loc: Location) { this.storedPath = loc.path(); } + constructor(loc: Location) { + this.storedPath = loc.path(); + } } @NgModule({declarations: [RecordLocationCmp], entryComponents: [RecordLocationCmp]}) @@ -632,11 +645,12 @@ describe('Integration', () => { providers: [{ provide: 'authGuardFail', useValue: (a: any, b: any) => { - return new Promise(res => { setTimeout(() => res(serializer.parse('/login')), 1); }); + return new Promise(res => { + setTimeout(() => res(serializer.parse('/login')), 1); + }); } }] }); - }); @@ -657,7 +671,7 @@ describe('Integration', () => { (router as any).hooks.beforePreactivation = () => { expect(location.path()).toEqual('/team/33'); expect(fixture.nativeElement).toHaveText('team 22 [ , right: ]'); - return of (null); + return of(null); }; router.navigateByUrl('/team/33'); @@ -758,7 +772,6 @@ describe('Integration', () => { it('should should set `state` with urlUpdateStrategy="eagar"', fakeAsync(inject([Router, Location], (router: Router, location: SpyLocation) => { - router.urlUpdateStrategy = 'eager'; router.resetConfig([ {path: '', component: SimpleCmp}, @@ -766,10 +779,10 @@ describe('Integration', () => { ]); const fixture = createRoot(router, RootCmp); - let navigation: Navigation = null !; + let navigation: Navigation = null!; router.events.subscribe(e => { if (e instanceof NavigationStart) { - navigation = router.getCurrentNavigation() !; + navigation = router.getCurrentNavigation()!; } }); @@ -806,24 +819,24 @@ describe('Integration', () => { router.navigateByUrl('/team/33/simple'); advance(fixture); expect(location.path()).toEqual('/team/33/simple'); - const simpleNavStart = event !; + const simpleNavStart = event!; router.navigateByUrl('/team/22/user/victor'); advance(fixture); - const userVictorNavStart = event !; + const userVictorNavStart = event!; location.back(); advance(fixture); expect(location.path()).toEqual('/team/33/simple'); - expect(event !.navigationTrigger).toEqual('hashchange'); - expect(event !.restoredState !.navigationId).toEqual(simpleNavStart.id); + expect(event!.navigationTrigger).toEqual('hashchange'); + expect(event!.restoredState!.navigationId).toEqual(simpleNavStart.id); location.forward(); advance(fixture); expect(location.path()).toEqual('/team/22/user/victor'); - expect(event !.navigationTrigger).toEqual('hashchange'); - expect(event !.restoredState !.navigationId).toEqual(userVictorNavStart.id); + expect(event!.navigationTrigger).toEqual('hashchange'); + expect(event!.restoredState!.navigationId).toEqual(userVictorNavStart.id); }))); it('should navigate to the same url when config changes', @@ -1115,8 +1128,8 @@ describe('Integration', () => { const user = fixture.debugElement.children[1].componentInstance; let r1: any, r2: any; - router.navigateByUrl('/user/victor') !.then(_ => r1 = _); - router.navigateByUrl('/user/fedor') !.then(_ => r2 = _); + router.navigateByUrl('/user/victor')!.then(_ => r1 = _); + router.navigateByUrl('/user/fedor')!.then(_ => r2 = _); advance(fixture); expect(r1).toEqual(false); // returns false because it was canceled @@ -1185,7 +1198,7 @@ describe('Integration', () => { router.events.forEach(e => recordedEvents.push(e)); let e: any; - router.navigateByUrl('/invalid') !.catch(_ => e = _); + router.navigateByUrl('/invalid')!.catch(_ => e = _); advance(fixture); expect(e.message).toContain('Cannot match any routes'); @@ -1254,7 +1267,7 @@ describe('Integration', () => { router.navigateByUrl('/simple1'); advance(fixture); - const simple1NavStart = event !; + const simple1NavStart = event!; router.navigateByUrl('/throwing').catch(() => null); advance(fixture); @@ -1265,7 +1278,7 @@ describe('Integration', () => { location.back(); tick(); - expect(event !.restoredState !.navigationId).toEqual(simple1NavStart.id); + expect(event!.restoredState!.navigationId).toEqual(simple1NavStart.id); })); it('should not trigger another navigation when resetting the url back due to a NavigationError', @@ -1295,7 +1308,6 @@ describe('Integration', () => { // we do not trigger another navigation to /simple expect(events).toEqual(['/simple', '/throwing']); })); - }); it('should dispatch NavigationCancel after the url has been reset back', fakeAsync(() => { @@ -1341,7 +1353,7 @@ describe('Integration', () => { router.events.forEach(e => recordedEvents.push(e)); let e: any; - router.navigateByUrl('/invalid') !.then(_ => e = _); + router.navigateByUrl('/invalid')!.then(_ => e = _); advance(fixture); expect(e).toEqual('resolvedValue'); @@ -1360,8 +1372,9 @@ describe('Integration', () => { it('should support custom malformed uri error handler', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { const customMalformedUriErrorHandler = - (e: URIError, urlSerializer: UrlSerializer, url: string): - UrlTree => { return urlSerializer.parse('/?error=The-URL-you-went-to-is-invalid'); }; + (e: URIError, urlSerializer: UrlSerializer, url: string): UrlTree => { + return urlSerializer.parse('/?error=The-URL-you-went-to-is-invalid'); + }; router.malformedUriErrorHandler = customMalformedUriErrorHandler; router.resetConfig([{path: 'simple', component: SimpleCmp}]); @@ -1482,9 +1495,13 @@ describe('Integration', () => { activations: any[] = []; deactivations: any[] = []; - recordActivate(component: any): void { this.activations.push(component); } + recordActivate(component: any): void { + this.activations.push(component); + } - recordDeactivate(component: any): void { this.deactivations.push(component); } + recordDeactivate(component: any): void { + this.deactivations.push(component); + } } TestBed.configureTestingModule({declarations: [Container]}); @@ -1517,7 +1534,6 @@ describe('Integration', () => { it('should update url and router state before activating components', fakeAsync(inject([Router], (router: Router) => { - const fixture = createRoot(router, RootCmp); router.resetConfig([{path: 'cmp', component: ComponentRecordingRoutePathAndUrl}]); @@ -1535,7 +1551,9 @@ describe('Integration', () => { describe('data', () => { class ResolveSix implements Resolve { - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): number { return 6; } + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): number { + return 6; + } } beforeEach(() => { @@ -1601,7 +1619,7 @@ describe('Integration', () => { router.events.subscribe(e => e instanceof RouterEvent && recordedEvents.push(e)); let e: any = null; - router.navigateByUrl('/simple') !.catch(error => e = error); + router.navigateByUrl('/simple')!.catch(error => e = error); advance(fixture); expectEvents(recordedEvents, [ @@ -1695,7 +1713,7 @@ describe('Integration', () => { { provide: 'resolver2', useValue: () => { - return of (null).pipe(map(() => { + return of(null).pipe(map(() => { log.push('resolver2'); observer.next(null); observer.complete(); @@ -1764,9 +1782,8 @@ describe('Integration', () => { router.resetConfig([{ path: 'team/:id', component: TeamCmp, - children: [ - {path: 'link', component: StringLinkCmp}, {path: 'simple', component: SimpleCmp} - ] + children: + [{path: 'link', component: StringLinkCmp}, {path: 'simple', component: SimpleCmp}] }]); router.navigateByUrl('/team/22/link'); @@ -1825,7 +1842,6 @@ describe('Integration', () => { })); it('should update hrefs when query params or fragment change', fakeAsync(() => { - @Component({ selector: 'someRoot', template: @@ -1855,7 +1871,6 @@ describe('Integration', () => { })); it('should correctly use the preserve strategy', fakeAsync(() => { - @Component({ selector: 'someRoot', template: @@ -1877,7 +1892,6 @@ describe('Integration', () => { })); it('should correctly use the merge strategy', fakeAsync(() => { - @Component({ selector: 'someRoot', template: @@ -1905,8 +1919,7 @@ describe('Integration', () => { path: 'team/:id', component: TeamCmp, children: [ - {path: 'link', component: StringLinkButtonCmp}, - {path: 'simple', component: SimpleCmp} + {path: 'link', component: StringLinkButtonCmp}, {path: 'simple', component: SimpleCmp} ] }]); @@ -1928,9 +1941,8 @@ describe('Integration', () => { router.resetConfig([{ path: 'team/:id', component: TeamCmp, - children: [ - {path: 'link', component: AbsoluteLinkCmp}, {path: 'simple', component: SimpleCmp} - ] + children: + [{path: 'link', component: AbsoluteLinkCmp}, {path: 'simple', component: SimpleCmp}] }]); router.navigateByUrl('/team/22/link'); @@ -1951,9 +1963,8 @@ describe('Integration', () => { router.resetConfig([{ path: 'team/:id', component: TeamCmp, - children: [ - {path: 'link', component: RelativeLinkCmp}, {path: 'simple', component: SimpleCmp} - ] + children: + [{path: 'link', component: RelativeLinkCmp}, {path: 'simple', component: SimpleCmp}] }]); router.navigateByUrl('/team/22/link'); @@ -2019,35 +2030,42 @@ describe('Integration', () => { expect(location.path()).toEqual('/team/22/simple?q=1#f'); }))); - it('should support history state', - fakeAsync(inject([Router, Location], (router: Router, location: SpyLocation) => { - const fixture = createRoot(router, RootCmp); + describe('should support history and state', () => { + let component: typeof LinkWithState|typeof DivLinkWithState; + it('for anchor elements', () => { + // Test logic in afterEach to reduce duplication + component = LinkWithState; + }); - router.resetConfig([{ - path: 'team/:id', - component: TeamCmp, - children: [ - {path: 'link', component: LinkWithState}, {path: 'simple', component: SimpleCmp} - ] - }]); + it('for non-anchor elements', () => { + // Test logic in afterEach to reduce duplication + component = DivLinkWithState; + }); - router.navigateByUrl('/team/22/link'); - advance(fixture); + afterEach(fakeAsync(inject([Router, Location], (router: Router, location: SpyLocation) => { + const fixture = createRoot(router, RootCmp); - const native = fixture.nativeElement.querySelector('a'); - expect(native.getAttribute('href')).toEqual('/team/22/simple'); - native.click(); - advance(fixture); + router.resetConfig([{ + path: 'team/:id', + component: TeamCmp, + children: [{path: 'link', component}, {path: 'simple', component: SimpleCmp}] + }]); - expect(fixture.nativeElement).toHaveText('team 22 [ simple, right: ]'); + router.navigateByUrl('/team/22/link'); + advance(fixture); - // Check the history entry - const history = (location as any)._history; + const native = fixture.nativeElement.querySelector('#link'); + native.click(); + advance(fixture); - expect(history[history.length - 1].state.foo).toBe('bar'); - expect(history[history.length - 1].state) - .toEqual({foo: 'bar', navigationId: history.length}); - }))); + expect(fixture.nativeElement).toHaveText('team 22 [ simple, right: ]'); + + // Check the history entry + const history = (location as any)._history; + expect(history[history.length - 1].state) + .toEqual({foo: 'bar', navigationId: history.length}); + }))); + }); it('should set href on area elements', fakeAsync(() => { @Component({ @@ -2232,7 +2250,9 @@ describe('Integration', () => { } } - beforeEach(() => { TestBed.configureTestingModule({providers: [AlwaysTrue]}); }); + beforeEach(() => { + TestBed.configureTestingModule({providers: [AlwaysTrue]}); + }); it('works', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { const fixture = createRoot(router, RootCmp); @@ -2253,7 +2273,9 @@ describe('Integration', () => { providers: [{ provide: 'CanActivate', useValue: (a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => { - return Observable.create((observer: any) => { observer.next(false); }); + return Observable.create((observer: any) => { + observer.next(false); + }); } }] }); @@ -2310,7 +2332,9 @@ describe('Integration', () => { TestBed.configureTestingModule({ providers: [{ provide: 'alwaysFalse', - useValue: (a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => { return false; } + useValue: (a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => { + return false; + } }] }); }); @@ -2330,7 +2354,6 @@ describe('Integration', () => { location.go('/two'); advance(fixture); expect(location.path()).toEqual('/one'); - }))); }); @@ -2368,12 +2391,16 @@ describe('Integration', () => { providers: [ { provide: 'returnUrlTree', - useFactory: (router: Router) => () => { return router.parseUrl('/redirected'); }, + useFactory: (router: Router) => () => { + return router.parseUrl('/redirected'); + }, deps: [Router] }, { provide: 'returnRootUrlTree', - useFactory: (router: Router) => () => { return router.parseUrl('/'); }, + useFactory: (router: Router) => () => { + return router.parseUrl('/'); + }, deps: [Router] } ] @@ -2381,7 +2408,7 @@ describe('Integration', () => { it('works', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { const recordedEvents: any[] = []; - let cancelEvent: NavigationCancel = null !; + let cancelEvent: NavigationCancel = null!; router.events.forEach((e: any) => { recordedEvents.push(e); if (e instanceof NavigationCancel) cancelEvent = e; @@ -2425,7 +2452,7 @@ describe('Integration', () => { it('works with root url', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { const recordedEvents: any[] = []; - let cancelEvent: NavigationCancel = null !; + let cancelEvent: NavigationCancel = null!; router.events.forEach((e: any) => { recordedEvents.push(e); if (e instanceof NavigationCancel) cancelEvent = e; @@ -2492,7 +2519,9 @@ describe('Integration', () => { {path: 'redirected', component: SimpleCmp} ]); const fixture = createRoot(router, RootCmp); - router.navigateByUrl('/one').then(v => { resolvedPath = location.path(); }); + router.navigateByUrl('/one').then(v => { + resolvedPath = location.path(); + }); tick(); expect(resolvedPath).toBe('/redirected'); @@ -2541,7 +2570,8 @@ describe('Integration', () => { }, { path: 'd/:param', - component: WrapperCmp, runGuardsAndResolvers, + component: WrapperCmp, + runGuardsAndResolvers, children: [ { path: 'e/:param', @@ -2883,14 +2913,15 @@ describe('Integration', () => { { provide: 'RecordingDeactivate', useValue: (c: any, a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => { - log.push({path: a.routeConfig !.path, component: c}); + log.push({path: a.routeConfig!.path, component: c}); return true; } }, { provide: 'alwaysFalse', - useValue: - (c: any, a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => { return false; } + useValue: (c: any, a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => { + return false; + } }, { provide: 'alwaysFalseAndLogging', @@ -2932,13 +2963,13 @@ describe('Integration', () => { expect(location.path()).toEqual('/team/22'); let successStatus: boolean = false; - router.navigateByUrl('/team/33') !.then(res => successStatus = res); + router.navigateByUrl('/team/33')!.then(res => successStatus = res); advance(fixture); expect(location.path()).toEqual('/team/33'); expect(successStatus).toEqual(true); let canceledStatus: boolean = false; - router.navigateByUrl('/team/44') !.then(res => canceledStatus = res); + router.navigateByUrl('/team/44')!.then(res => canceledStatus = res); advance(fixture); expect(location.path()).toEqual('/team/33'); expect(canceledStatus).toEqual(false); @@ -3143,11 +3174,12 @@ describe('Integration', () => { providers: [ ClassWithNextState, { provide: 'FunctionWithNextState', - useValue: (cmp: any, currentRoute: ActivatedRouteSnapshot, - currentState: RouterStateSnapshot, nextState: RouterStateSnapshot) => { - log.push(currentState.url, nextState.url); - return true; - } + useValue: + (cmp: any, currentRoute: ActivatedRouteSnapshot, + currentState: RouterStateSnapshot, nextState: RouterStateSnapshot) => { + log.push(currentState.url, nextState.url); + return true; + } } ] }); @@ -3198,7 +3230,9 @@ describe('Integration', () => { } } - beforeEach(() => { TestBed.configureTestingModule({providers: [AlwaysTrue]}); }); + beforeEach(() => { + TestBed.configureTestingModule({providers: [AlwaysTrue]}); + }); it('works', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { const fixture = createRoot(router, RootCmp); @@ -3223,7 +3257,9 @@ describe('Integration', () => { providers: [{ provide: 'CanDeactivate', useValue: (c: TeamCmp, a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => { - return Observable.create((observer: any) => { observer.next(false); }); + return Observable.create((observer: any) => { + observer.next(false); + }); } }] }); @@ -3271,7 +3307,7 @@ describe('Integration', () => { expect(location.path()).toEqual('/team/22'); - router.navigateByUrl('/team/33') !.catch(() => {}); + router.navigateByUrl('/team/33')!.catch(() => {}); advance(fixture); expect(location.path()).toEqual('/team/22'); @@ -3282,7 +3318,6 @@ describe('Integration', () => { fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'admin', template: ''}) class AdminComponent { } @@ -3350,7 +3385,6 @@ describe('Integration', () => { fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) class LazyLoadedComponent { } @@ -3418,7 +3452,6 @@ describe('Integration', () => { it('should support navigating from within the guard', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { - const fixture = createRoot(router, RootCmp); router.resetConfig([ @@ -3454,7 +3487,6 @@ describe('Integration', () => { fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) class LazyLoadedComponent { } @@ -3477,21 +3509,23 @@ describe('Integration', () => { let navFalseResult: any; let navTrueResult: any; - router.navigateByUrl('/lazy-false').then(v => { navFalseResult = v; }); + router.navigateByUrl('/lazy-false').then(v => { + navFalseResult = v; + }); advance(fixture); - router.navigateByUrl('/lazy-true').then(v => { navTrueResult = v; }); + router.navigateByUrl('/lazy-true').then(v => { + navTrueResult = v; + }); advance(fixture); expect(navFalseResult).toBe(false); expect(navTrueResult).toBe(true); - }))); it('should execute CanLoad only once', fakeAsync(inject( [Router, Location, NgModuleFactoryLoader], (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) class LazyLoadedComponent { } @@ -3526,10 +3560,11 @@ describe('Integration', () => { }); describe('order', () => { - class Logger { logs: string[] = []; - add(thing: string) { this.logs.push(thing); } + add(thing: string) { + this.logs.push(thing); + } } beforeEach(() => { @@ -3664,7 +3699,7 @@ describe('Integration', () => { tap(e => recordedEvents.push(e)), filter((e): e is NavigationStart => e instanceof NavigationStart), first()); - navStart$.subscribe((e: NavigationStart | NavigationError) => { + navStart$.subscribe((e: NavigationStart|NavigationError) => { router.navigate( ['/blank'], {queryParams: {state: 'redirected'}, queryParamsHandling: 'merge'}); advance(fixture); @@ -3674,7 +3709,6 @@ describe('Integration', () => { advance(fixture); expect(navigateSpy.calls.mostRecent().args[1].queryParams); - }))); }); @@ -3689,8 +3723,7 @@ describe('Integration', () => { children: [{ path: 'link', component: DummyLinkCmp, - children: - [{path: 'simple', component: SimpleCmp}, {path: '', component: BlankCmp}] + children: [{path: 'simple', component: SimpleCmp}, {path: '', component: BlankCmp}] }] }]); @@ -3745,8 +3778,7 @@ describe('Integration', () => { children: [{ path: 'link', component: DummyLinkWithParentCmp, - children: - [{path: 'simple', component: SimpleCmp}, {path: '', component: BlankCmp}] + children: [{path: 'simple', component: SimpleCmp}, {path: '', component: BlankCmp}] }] }]); @@ -3774,8 +3806,7 @@ describe('Integration', () => { children: [{ path: 'link', component: DummyLinkCmp, - children: - [{path: 'simple', component: SimpleCmp}, {path: '', component: BlankCmp}] + children: [{path: 'simple', component: SimpleCmp}, {path: '', component: BlankCmp}] }] }]); @@ -3831,7 +3862,6 @@ describe('Integration', () => { advance(fixture); expect(paragraph.textContent).toEqual('false'); })); - }); describe('lazy loading', () => { @@ -3934,8 +3964,8 @@ describe('Integration', () => { expect(location.path()).toEqual('/lazy/parent/child'); expect(fixture.nativeElement).toHaveText('parent[child]'); - const pInj = fixture.debugElement.query(By.directive(Parent)).injector !; - const cInj = fixture.debugElement.query(By.directive(Child)).injector !; + const pInj = fixture.debugElement.query(By.directive(Parent)).injector!; + const cInj = fixture.debugElement.query(By.directive(Child)).injector!; expect(pInj.get('moduleName')).toEqual('parent'); expect(pInj.get('fromParent')).toEqual('from parent'); @@ -3989,7 +4019,9 @@ describe('Integration', () => { }) class LoadedModule { static instances = 0; - constructor() { LoadedModule.instances++; } + constructor() { + LoadedModule.instances++; + } } loader.stubbedModules = {expected: LoadedModule}; @@ -4121,7 +4153,7 @@ describe('Integration', () => { router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]); let recordedError: any = null; - router.navigateByUrl('/lazy/loaded') !.catch(err => recordedError = err); + router.navigateByUrl('/lazy/loaded')!.catch(err => recordedError = err); advance(fixture); expect(recordedError.message) .toEqual( @@ -4178,7 +4210,6 @@ describe('Integration', () => { it('should allow lazy loaded module in named outlet', fakeAsync(inject( [Router, NgModuleFactoryLoader], (router: Router, loader: SpyNgModuleFactoryLoader) => { - @Component({selector: 'lazy', template: 'lazy-loaded'}) class LazyComponent { } @@ -4218,7 +4249,6 @@ describe('Integration', () => { it('should allow componentless named outlet to render children', fakeAsync(inject( [Router, NgModuleFactoryLoader], (router: Router, loader: SpyNgModuleFactoryLoader) => { - const fixture = createRoot(router, RootCmp); router.resetConfig([{ @@ -4269,7 +4299,7 @@ describe('Integration', () => { lazyService: LazyLoadedServiceDefinedInModule, // should be able to inject lazy service eager: EagerParentComponent // should use the injector of the location to create a parent - ) {} + ) {} } @NgModule({ @@ -4360,7 +4390,7 @@ describe('Integration', () => { const recordedEvents: any[] = []; router.events.forEach(e => recordedEvents.push(e)); - router.navigateByUrl('/lazy/loaded') !.catch(s => {}); + router.navigateByUrl('/lazy/loaded')!.catch(s => {}); advance(fixture); expect(location.path()).toEqual('/'); @@ -4469,16 +4499,15 @@ describe('Integration', () => { advance(fixture); const config = router.config as any; - const firstConfig = config[1]._loadedConfig !; + const firstConfig = config[1]._loadedConfig!; expect(firstConfig).toBeDefined(); expect(firstConfig.routes[0].path).toEqual('LoadedModule1'); - const secondConfig = firstConfig.routes[0]._loadedConfig !; + const secondConfig = firstConfig.routes[0]._loadedConfig!; expect(secondConfig).toBeDefined(); expect(secondConfig.routes[0].path).toEqual('LoadedModule2'); }))); - }); describe('custom url handling strategies', () => { @@ -4528,9 +4557,8 @@ describe('Integration', () => { router.resetConfig([{ path: 'include', component: TeamCmp, - children: [ - {path: 'user/:name', component: UserCmp}, {path: 'simple', component: SimpleCmp} - ] + children: + [{path: 'user/:name', component: UserCmp}, {path: 'simple', component: SimpleCmp}] }]); const events: any[] = []; @@ -4592,9 +4620,8 @@ describe('Integration', () => { router.resetConfig([{ path: 'include', component: TeamCmp, - children: [ - {path: 'user/:name', component: UserCmp}, {path: 'simple', component: SimpleCmp} - ] + children: + [{path: 'user/:name', component: UserCmp}, {path: 'simple', component: SimpleCmp}] }]); const events: any[] = []; @@ -4686,19 +4713,19 @@ describe('Integration', () => { stored: {[k: string]: DetachedRouteHandle} = {}; shouldDetach(route: ActivatedRouteSnapshot): boolean { - return route.routeConfig !.path === 'a'; + return route.routeConfig!.path === 'a'; } store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void { - this.stored[route.routeConfig !.path !] = detachedTree; + this.stored[route.routeConfig!.path!] = detachedTree; } shouldAttach(route: ActivatedRouteSnapshot): boolean { - return !!this.stored[route.routeConfig !.path !]; + return !!this.stored[route.routeConfig!.path!]; } retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { - return this.stored[route.routeConfig !.path !]; + return this.stored[route.routeConfig!.path!]; } shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { @@ -4707,10 +4734,16 @@ describe('Integration', () => { } class ShortLifecycle implements RouteReuseStrategy { - shouldDetach(route: ActivatedRouteSnapshot): boolean { return false; } + shouldDetach(route: ActivatedRouteSnapshot): boolean { + return false; + } store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void {} - shouldAttach(route: ActivatedRouteSnapshot): boolean { return false; } - retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle|null { return null; } + shouldAttach(route: ActivatedRouteSnapshot): boolean { + return false; + } + retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle|null { + return null; + } shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { if (future.routeConfig !== curr.routeConfig) { return false; @@ -4804,7 +4837,9 @@ describe('Integration', () => { !!router.parseUrl(router.url).root.children['toolpanel']); } - public ngOnDestroy(): void { this.subscription.unsubscribe(); } + public ngOnDestroy(): void { + this.subscription.unsubscribe(); + } } @Component({selector: 'tool-1-cmp', template: 'Tool 1 showing'}) @@ -4876,7 +4911,6 @@ describe('Testing router options', () => { }); describe('malformedUriErrorHandler', () => { - function malformedUriErrorHandler(e: URIError, urlSerializer: UrlSerializer, url: string) { return urlSerializer.parse('/error'); } @@ -4951,11 +4985,18 @@ class LinkWithQueryParamsAndFragment { @Component({ selector: 'link-cmp', - template: `link` + template: `link` }) class LinkWithState { } +@Component({ + selector: 'div-link-cmp', + template: `` +}) +class DivLinkWithState { +} + @Component({selector: 'simple-cmp', template: `simple`}) class SimpleCmp { } @@ -5078,7 +5119,9 @@ class OutletInNgIf { }) class DummyLinkWithParentCmp { private exact: boolean; - constructor(route: ActivatedRoute) { this.exact = (route.snapshot.params).exact === 'true'; } + constructor(route: ActivatedRoute) { + this.exact = (route.snapshot.params).exact === 'true'; + } } @Component({selector: 'cmp', template: ''}) @@ -5100,7 +5143,9 @@ class RootCmp { class RootCmpWithOnInit { constructor(private router: Router) {} - ngOnInit(): void { this.router.navigate(['one']); } + ngOnInit(): void { + this.router.navigate(['one']); + } } @Component({ @@ -5117,7 +5162,9 @@ class RootCmpWithNamedOutlet { @Component({selector: 'throwing-cmp', template: ''}) class ThrowingCmp { - constructor() { throw new Error('Throwing Cmp'); } + constructor() { + throw new Error('Throwing Cmp'); + } } @@ -5155,6 +5202,7 @@ class LazyComponent { RelativeLinkCmp, DummyLinkWithParentCmp, LinkWithQueryParamsAndFragment, + DivLinkWithState, LinkWithState, CollectParamsCmp, QueryParamsAndFragmentCmp, @@ -5185,6 +5233,7 @@ class LazyComponent { RelativeLinkCmp, DummyLinkWithParentCmp, LinkWithQueryParamsAndFragment, + DivLinkWithState, LinkWithState, CollectParamsCmp, QueryParamsAndFragmentCmp, @@ -5217,6 +5266,7 @@ class LazyComponent { RelativeLinkCmp, DummyLinkWithParentCmp, LinkWithQueryParamsAndFragment, + DivLinkWithState, LinkWithState, CollectParamsCmp, QueryParamsAndFragmentCmp,