Skip to content

Commit eb2e879

Browse files
committed
refactor(router): Update integration tests to cover navigation and history API (angular#53799)
This commit updates the router integration tests to cover both the classic History and the new Navigation API. There is more work to be done here, but this commit works to prove the efficacy of the `FakeNavigation` implementation. PR Close angular#53799
1 parent 2dedc4a commit eb2e879

File tree

7 files changed

+6028
-5896
lines changed

7 files changed

+6028
-5896
lines changed

packages/common/src/private_export.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
*/
88

99
export {DomAdapter as ɵDomAdapter, getDOM as ɵgetDOM, setRootDomAdapter as ɵsetRootDomAdapter} from './dom_adapter';
10+
export {PlatformNavigation as ɵPlatformNavigation} from './navigation/platform_navigation';

packages/common/testing/src/mock_platform_location.ts

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {LocationChangeEvent, LocationChangeListener, PlatformLocation} from '@angular/common';
10-
import {Inject, Injectable, InjectionToken, Optional} from '@angular/core';
9+
import {DOCUMENT, LocationChangeEvent, LocationChangeListener, PlatformLocation, ɵPlatformNavigation as PlatformNavigation} from '@angular/common';
10+
import {Inject, inject, Injectable, InjectionToken, Optional} from '@angular/core';
1111
import {Subject} from 'rxjs';
1212

13+
import {FakeNavigation} from './navigation/fake_navigation';
14+
1315
/**
1416
* Parser from https://tools.ietf.org/html/rfc3986#appendix-B
1517
* ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
@@ -250,3 +252,82 @@ export class MockPlatformLocation implements PlatformLocation {
250252
}
251253
}
252254
}
255+
256+
/**
257+
* Mock implementation of URL state.
258+
*/
259+
@Injectable()
260+
export class FakeNavigationPlatformLocation implements PlatformLocation {
261+
private _platformNavigation = inject(PlatformNavigation) as FakeNavigation;
262+
private window = inject(DOCUMENT).defaultView!;
263+
264+
constructor() {
265+
if (!(this._platformNavigation instanceof FakeNavigation)) {
266+
throw new Error(
267+
'FakePlatformNavigation cannot be used without FakeNavigation. Use ' +
268+
'`provideFakeNavigation` to have all these services provided together.',
269+
);
270+
}
271+
}
272+
273+
private config = inject(MOCK_PLATFORM_LOCATION_CONFIG, {optional: true});
274+
getBaseHrefFromDOM(): string {
275+
return this.config?.appBaseHref ?? '';
276+
}
277+
278+
onPopState(fn: LocationChangeListener): VoidFunction {
279+
this.window.addEventListener('popstate', fn);
280+
return () => this.window.removeEventListener('popstate', fn);
281+
}
282+
283+
onHashChange(fn: LocationChangeListener): VoidFunction {
284+
this.window.addEventListener('hashchange', fn as any);
285+
return () => this.window.removeEventListener('hashchange', fn as any);
286+
}
287+
288+
get href(): string {
289+
return this._platformNavigation.currentEntry.url!;
290+
}
291+
get protocol(): string {
292+
return new URL(this._platformNavigation.currentEntry.url!).protocol;
293+
}
294+
get hostname(): string {
295+
return new URL(this._platformNavigation.currentEntry.url!).hostname;
296+
}
297+
get port(): string {
298+
return new URL(this._platformNavigation.currentEntry.url!).port;
299+
}
300+
get pathname(): string {
301+
return new URL(this._platformNavigation.currentEntry.url!).pathname;
302+
}
303+
get search(): string {
304+
return new URL(this._platformNavigation.currentEntry.url!).search;
305+
}
306+
get hash(): string {
307+
return new URL(this._platformNavigation.currentEntry.url!).hash;
308+
}
309+
310+
pushState(state: any, title: string, url: string): void {
311+
this._platformNavigation.pushState(state, title, url);
312+
}
313+
314+
replaceState(state: any, title: string, url: string): void {
315+
this._platformNavigation.replaceState(state, title, url);
316+
}
317+
318+
forward(): void {
319+
this._platformNavigation.forward();
320+
}
321+
322+
back(): void {
323+
this._platformNavigation.back();
324+
}
325+
326+
historyGo(relativePosition: number = 0): void {
327+
this._platformNavigation.go(relativePosition);
328+
}
329+
330+
getState(): unknown {
331+
return this._platformNavigation.currentEntry.getHistoryState();
332+
}
333+
}

packages/common/testing/src/navigation/fake_navigation.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
// Prevents deletion of `Event` from `globalThis` during module loading.
10-
const Event = globalThis.Event;
11-
129
/**
1310
* Fake implementation of user agent history and navigation behavior. This is a
1411
* high-fidelity implementation of browser behavior that attempts to emulate

packages/common/testing/src/navigation/provide_fake_platform_navigation.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,29 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Provider} from '@angular/core';
9+
import {DOCUMENT, PlatformLocation} from '@angular/common';
10+
import {inject, Provider} from '@angular/core';
1011

1112
// @ng_package: ignore-cross-repo-import
1213
import {PlatformNavigation} from '../../../src/navigation/platform_navigation';
14+
import {FakeNavigationPlatformLocation, MOCK_PLATFORM_LOCATION_CONFIG} from '../mock_platform_location';
1315

1416
import {FakeNavigation} from './fake_navigation';
1517

1618
/**
1719
* Return a provider for the `FakeNavigation` in place of the real Navigation API.
18-
*
19-
* @internal
2020
*/
2121
export function provideFakePlatformNavigation(): Provider[] {
2222
return [
2323
{
2424
provide: PlatformNavigation,
2525
useFactory: () => {
26-
return new FakeNavigation(window, 'https://test.com');
26+
const config = inject(MOCK_PLATFORM_LOCATION_CONFIG, {optional: true});
27+
return new FakeNavigation(
28+
inject(DOCUMENT).defaultView!,
29+
config?.startUrl as `http${string}` ?? 'http://_empty_/');
2730
}
2831
},
32+
{provide: PlatformLocation, useClass: FakeNavigationPlatformLocation},
2933
];
3034
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export {provideFakePlatformNavigation as ɵprovideFakePlatformNavigation} from './navigation/provide_fake_platform_navigation';

packages/common/testing/src/testing.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
* @description
1212
* Entry point for all public APIs of the common/testing package.
1313
*/
14+
15+
export * from './private_export';
1416
export {SpyLocation} from './location_mock';
1517
export {MockLocationStrategy} from './mock_location_strategy';
1618
export {MOCK_PLATFORM_LOCATION_CONFIG, MockPlatformLocation, MockPlatformLocationConfig} from './mock_platform_location';

0 commit comments

Comments
 (0)