mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
50d7916278
With the deprecation of the configurable errorHandler in the Router, there is a missing use-case to prevent the navigation promise from rejecting on an error. This rejection results in unhandled promise rejections. This commit allows developers to instruct the router to instead resolve the navigation promise with 'false', which matches the behavior of other failed navigations. Resolving the Promise would be the ideal default behavior. It is rare that any code handles the navigation Promise at all and even more rare that the Promise rejection is caught. Updating the default value for this option should be considered for an upcoming major version. fixes #48902 PR Close #48910
148 lines
4.8 KiB
TypeScript
148 lines
4.8 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 {AsyncPipe} from '@angular/common';
|
|
import {Component, inject} from '@angular/core';
|
|
import {TestBed} from '@angular/core/testing';
|
|
import {ActivatedRoute, provideRouter, Router} from '@angular/router';
|
|
import {RouterTestingHarness} from '@angular/router/testing';
|
|
import {of} from 'rxjs';
|
|
import {delay} from 'rxjs/operators';
|
|
|
|
import {withRouterConfig} from '../../src/provide_router';
|
|
|
|
describe('navigateForTest', () => {
|
|
it('gives null for the activatedComponent when no routes are configured', async () => {
|
|
TestBed.configureTestingModule({providers: [provideRouter([])]});
|
|
const harness = await RouterTestingHarness.create('/');
|
|
expect(harness.routeDebugElement).toBeNull();
|
|
});
|
|
it('navigates to routed component', async () => {
|
|
@Component({standalone: true, template: 'hello {{name}}'})
|
|
class TestCmp {
|
|
name = 'world';
|
|
}
|
|
|
|
TestBed.configureTestingModule({providers: [provideRouter([{path: '', component: TestCmp}])]});
|
|
const harness = await RouterTestingHarness.create();
|
|
const activatedComponent = await harness.navigateByUrl('/', TestCmp);
|
|
|
|
expect(activatedComponent).toBeInstanceOf(TestCmp);
|
|
expect(harness.routeNativeElement?.innerHTML).toContain('hello world');
|
|
});
|
|
|
|
it('executes guards on the path', async () => {
|
|
let guardCalled = false;
|
|
TestBed.configureTestingModule({
|
|
providers: [provideRouter([{
|
|
path: '',
|
|
canActivate: [() => {
|
|
guardCalled = true;
|
|
return true;
|
|
}],
|
|
children: []
|
|
}])]
|
|
});
|
|
await RouterTestingHarness.create('/');
|
|
expect(guardCalled).toBeTrue();
|
|
});
|
|
|
|
it('throws error if routing throws', async () => {
|
|
TestBed.configureTestingModule({
|
|
providers: [provideRouter(
|
|
[
|
|
{
|
|
path: 'e',
|
|
canActivate: [() => {
|
|
throw new Error('oh no');
|
|
}],
|
|
children: []
|
|
},
|
|
],
|
|
withRouterConfig({resolveNavigationPromiseOnError: true}))]
|
|
});
|
|
const harness = await RouterTestingHarness.create();
|
|
await expectAsync(harness.navigateByUrl('e')).toBeResolvedTo(null);
|
|
});
|
|
|
|
it('can observe param changes on routed component with second navigation', async () => {
|
|
@Component({standalone: true, template: '{{(route.params | async)?.id}}', imports: [AsyncPipe]})
|
|
class TestCmp {
|
|
constructor(readonly route: ActivatedRoute) {}
|
|
}
|
|
|
|
TestBed.configureTestingModule({
|
|
providers: [
|
|
provideRouter([{path: ':id', component: TestCmp}]),
|
|
]
|
|
});
|
|
const harness = await RouterTestingHarness.create();
|
|
const activatedComponent = await harness.navigateByUrl('/123', TestCmp);
|
|
expect(activatedComponent.route).toBeInstanceOf(ActivatedRoute);
|
|
expect(harness.routeNativeElement?.innerHTML).toContain('123');
|
|
await harness.navigateByUrl('/456');
|
|
expect(harness.routeNativeElement?.innerHTML).toContain('456');
|
|
});
|
|
|
|
it('throws an error if the routed component instance does not match the one required',
|
|
async () => {
|
|
@Component({standalone: true, template: ''})
|
|
class TestCmp {
|
|
}
|
|
@Component({standalone: true, template: ''})
|
|
class OtherCmp {
|
|
}
|
|
|
|
TestBed.configureTestingModule({
|
|
providers: [
|
|
provideRouter([{path: '**', component: TestCmp}]),
|
|
]
|
|
});
|
|
const harness = await RouterTestingHarness.create();
|
|
await expectAsync(harness.navigateByUrl('/123', OtherCmp)).toBeRejected();
|
|
});
|
|
|
|
it('throws an error if navigation fails but expected a component instance', async () => {
|
|
@Component({standalone: true, template: ''})
|
|
class TestCmp {
|
|
}
|
|
|
|
TestBed.configureTestingModule({
|
|
providers: [
|
|
provideRouter([{path: '**', canActivate: [() => false], component: TestCmp}]),
|
|
]
|
|
});
|
|
const harness = await RouterTestingHarness.create();
|
|
await expectAsync(harness.navigateByUrl('/123', TestCmp)).toBeRejected();
|
|
});
|
|
|
|
it('waits for redirects using router.navigate', async () => {
|
|
@Component({standalone: true, template: 'test'})
|
|
class TestCmp {
|
|
}
|
|
@Component({standalone: true, template: 'redirect'})
|
|
class OtherCmp {
|
|
}
|
|
|
|
TestBed.configureTestingModule({
|
|
providers: [
|
|
provideRouter([
|
|
{
|
|
path: 'test',
|
|
canActivate: [() => inject(Router).navigateByUrl('/redirect')],
|
|
component: TestCmp
|
|
},
|
|
{path: 'redirect', canActivate: [() => of(true).pipe(delay(100))], component: OtherCmp},
|
|
]),
|
|
]
|
|
});
|
|
await RouterTestingHarness.create('test');
|
|
expect(TestBed.inject(Router).url).toEqual('/redirect');
|
|
});
|
|
});
|