From c7859a1ac1191c9625f54ddb5cd05bcd06506c0b Mon Sep 17 00:00:00 2001 From: jnizet Date: Tue, 14 Jun 2022 15:34:16 +0200 Subject: [PATCH] fix(route): make sure param maps contain the same values as params The ActivatedRouteSnapshot class of Angular only creates the param maps once, lazily, when it's requested for the first time. But the params are public, so changing them changes the params without changing the param maps. This commit circumvents this Angular bug by overriding the map getters in order to always return a new map created from the params. --- projects/ngx-speculoos/src/lib/route.spec.ts | 28 ++++++++++++++++++++ projects/ngx-speculoos/src/lib/route.ts | 12 ++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/projects/ngx-speculoos/src/lib/route.spec.ts b/projects/ngx-speculoos/src/lib/route.spec.ts index 37040527..d34846bb 100644 --- a/projects/ngx-speculoos/src/lib/route.spec.ts +++ b/projects/ngx-speculoos/src/lib/route.spec.ts @@ -305,6 +305,19 @@ describe('routes', () => { expect(queryParamEmissionCount).toBe(1); }); + it('should set a param in the map too, even if the map has already been read before', () => { + const route = stubRoute(); + + expect(route.snapshot.paramMap.get('a')).toBeNull(); + + const expectedParams = { a: 'a2' }; + + route.setParam('a', 'a2'); + + expect(route.snapshot.params).toEqual(expectedParams); + expect(route.snapshot.paramMap.get('a')).toBe('a2'); + }); + it('should set a query param', () => { const route = stubRoute({ queryParams: { a: 'a1', b: 'b1' } }); @@ -316,6 +329,21 @@ describe('routes', () => { expect(route.snapshot.queryParams).toEqual(expectedQueryParams); expect(queryParams).toEqual(expectedQueryParams); + expect(route.snapshot.queryParamMap.get('a')).toBe('a2'); + expect(route.snapshot.queryParamMap.get('b')).toBe('b1'); + }); + + it('should set a query param in the map too, even if the map has already been read before', () => { + const route = stubRoute(); + + expect(route.snapshot.queryParamMap.get('a')).toBeNull(); + + const expectedQueryParams = { a: 'a2' }; + + route.setQueryParam('a', 'a2'); + + expect(route.snapshot.queryParams).toEqual(expectedQueryParams); + expect(route.snapshot.queryParamMap.get('a')).toBe('a2'); }); it('should set a datum', () => { diff --git a/projects/ngx-speculoos/src/lib/route.ts b/projects/ngx-speculoos/src/lib/route.ts index 7dc1d48a..0bde618a 100644 --- a/projects/ngx-speculoos/src/lib/route.ts +++ b/projects/ngx-speculoos/src/lib/route.ts @@ -1,4 +1,4 @@ -import { ActivatedRoute, ActivatedRouteSnapshot, convertToParamMap, Data, Params, Route, UrlSegment } from '@angular/router'; +import { ActivatedRoute, ActivatedRouteSnapshot, convertToParamMap, Data, ParamMap, Params, Route, UrlSegment } from '@angular/router'; import { BehaviorSubject, map, Observable } from 'rxjs'; import { Type } from '@angular/core'; @@ -243,6 +243,14 @@ class ActivatedRouteSnapshotStub extends ActivatedRouteSnapshot { this._routeConfig = route; } + get paramMap(): ParamMap { + return convertToParamMap(this.params); + } + + get queryParamMap(): ParamMap { + return convertToParamMap(this.queryParams); + } + constructor() { super(); this._root = this; @@ -262,6 +270,8 @@ class ActivatedRouteSnapshotStub extends ActivatedRouteSnapshot { * - when changing the params, query params, fragment, etc., their associated observable emits unconditionally, instead of * first checking if the value is actually different from before. It's thus the responsibility of the tester to not * change the values if they're the same as before. + * - the params, paramMap, queryParams and queryParamMap objects of the route snapshot change when params or query params are set + * on the stub route. So if the code keeps a reference to params or paramMaps, it won't see the changes. */ export class ActivatedRouteStub extends ActivatedRoute { private _firstChild: ActivatedRouteStub | null;