fix(router): support redirects to named outlets

Closes #12740, #9921
This commit is contained in:
vsavkin
2016-11-16 17:43:04 -08:00
committed by Chuck Jazdzewski
parent 6c3166e6e4
commit 09226d96f8
3 changed files with 198 additions and 103 deletions

View File

@ -15,6 +15,7 @@ import {LoadedRouterConfig} from '../src/router_config_loader';
import {DefaultUrlSerializer, UrlSegmentGroup, UrlTree, equalSegments} from '../src/url_tree';
describe('applyRedirects', () => {
const serializer = new DefaultUrlSerializer();
it('should return the same url tree when no redirects', () => {
checkRedirect(
@ -38,7 +39,7 @@ describe('applyRedirects', () => {
});
it('should throw when cannot handle a positional parameter', () => {
applyRedirects(null, null, tree('/a/1'), [
applyRedirects(null, null, serializer, tree('/a/1'), [
{path: 'a/:id', redirectTo: 'a/:other'}
]).subscribe(() => {}, (e) => {
expect(e.message).toEqual('Cannot redirect to \'a/:other\'. Cannot find \':other\'.');
@ -133,11 +134,11 @@ describe('applyRedirects', () => {
{
path: 'a',
component: ComponentA,
children: [{path: 'b/:id', redirectTo: '/absolute/:id'}]
children: [{path: 'b/:id', redirectTo: '/absolute/:id?a=1&b=:b#f1'}]
},
{path: '**', component: ComponentC}
],
'/a/b/1', (t: UrlTree) => { compareTrees(t, tree('/absolute/1')); });
'/a/b/1?b=2', (t: UrlTree) => { compareTrees(t, tree('/absolute/1?a=1&b=2#f1')); });
});
describe('lazy loading', () => {
@ -153,10 +154,11 @@ describe('applyRedirects', () => {
};
const config = [{path: 'a', component: ComponentA, loadChildren: 'children'}];
applyRedirects(<any>'providedInjector', <any>loader, tree('a/b'), config).forEach(r => {
compareTrees(r, tree('/a/b'));
expect((<any>config[0])._loadedConfig).toBe(loadedConfig);
});
applyRedirects(<any>'providedInjector', <any>loader, serializer, tree('a/b'), config)
.forEach(r => {
compareTrees(r, tree('/a/b'));
expect((<any>config[0])._loadedConfig).toBe(loadedConfig);
});
});
it('should handle the case when the loader errors', () => {
@ -165,9 +167,8 @@ describe('applyRedirects', () => {
};
const config = [{path: 'a', component: ComponentA, loadChildren: 'children'}];
applyRedirects(null, <any>loader, tree('a/b'), config).subscribe(() => {}, (e) => {
expect(e.message).toEqual('Loading Error');
});
applyRedirects(null, <any>loader, serializer, tree('a/b'), config)
.subscribe(() => {}, (e) => { expect(e.message).toEqual('Loading Error'); });
});
it('should load when all canLoad guards return true', () => {
@ -186,7 +187,7 @@ describe('applyRedirects', () => {
loadChildren: 'children'
}];
applyRedirects(<any>injector, <any>loader, tree('a/b'), config).forEach(r => {
applyRedirects(<any>injector, <any>loader, serializer, tree('a/b'), config).forEach(r => {
compareTrees(r, tree('/a/b'));
});
});
@ -208,7 +209,7 @@ describe('applyRedirects', () => {
loadChildren: 'children'
}];
applyRedirects(<any>injector, <any>loader, tree('a/b'), config)
applyRedirects(<any>injector, <any>loader, serializer, tree('a/b'), config)
.subscribe(
() => { throw 'Should not reach'; },
(e) => {
@ -234,7 +235,7 @@ describe('applyRedirects', () => {
loadChildren: 'children'
}];
applyRedirects(<any>injector, <any>loader, tree('a/b'), config)
applyRedirects(<any>injector, <any>loader, serializer, tree('a/b'), config)
.subscribe(
() => { throw 'Should not reach'; }, (e) => { expect(e).toEqual('someError'); });
});
@ -251,7 +252,7 @@ describe('applyRedirects', () => {
const config =
[{path: 'a', component: ComponentA, canLoad: ['guard'], loadChildren: 'children'}];
applyRedirects(<any>injector, <any>loader, tree('a/b'), config)
applyRedirects(<any>injector, <any>loader, serializer, tree('a/b'), config)
.subscribe(
(r) => { compareTrees(r, tree('/a/b')); }, (e) => { throw 'Should not reach'; });
@ -267,10 +268,11 @@ describe('applyRedirects', () => {
const config =
[{path: '', pathMatch: 'full', redirectTo: '/a'}, {path: 'a', loadChildren: 'children'}];
applyRedirects(<any>'providedInjector', <any>loader, tree(''), config).forEach(r => {
compareTrees(r, tree('a'));
expect((<any>config[1])._loadedConfig).toBe(loadedConfig);
});
applyRedirects(<any>'providedInjector', <any>loader, serializer, tree(''), config)
.forEach(r => {
compareTrees(r, tree('a'));
expect((<any>config[1])._loadedConfig).toBe(loadedConfig);
});
});
it('should load the configuration only once', () => {
@ -289,12 +291,13 @@ describe('applyRedirects', () => {
const config = [{path: 'a', loadChildren: 'children'}];
applyRedirects(<any>'providedInjector', <any>loader, tree('a?k1'), config).subscribe(r => {});
applyRedirects(<any>'providedInjector', <any>loader, serializer, tree('a?k1'), config)
.subscribe(r => {});
applyRedirects(<any>'providedInjector', <any>loader, tree('a?k2'), config)
applyRedirects(<any>'providedInjector', <any>loader, serializer, tree('a?k2'), config)
.subscribe(
r => {
compareTrees(r, tree('a'));
compareTrees(r, tree('a?k2'));
expect((<any>config[0])._loadedConfig).toBe(loadedConfig);
},
(e) => { throw 'Should not reach'; });
@ -309,9 +312,8 @@ describe('applyRedirects', () => {
const config = [{path: '**', loadChildren: 'children'}];
applyRedirects(<any>'providedInjector', <any>loader, tree('xyz'), config).forEach(r => {
expect((<any>config[0])._loadedConfig).toBe(loadedConfig);
});
applyRedirects(<any>'providedInjector', <any>loader, serializer, tree('xyz'), config)
.forEach(r => { expect((<any>config[0])._loadedConfig).toBe(loadedConfig); });
});
it('should load the configuration after a local redirect from a wildcard route', () => {
@ -324,9 +326,8 @@ describe('applyRedirects', () => {
const config =
[{path: 'not-found', loadChildren: 'children'}, {path: '**', redirectTo: 'not-found'}];
applyRedirects(<any>'providedInjector', <any>loader, tree('xyz'), config).forEach(r => {
expect((<any>config[0])._loadedConfig).toBe(loadedConfig);
});
applyRedirects(<any>'providedInjector', <any>loader, serializer, tree('xyz'), config)
.forEach(r => { expect((<any>config[0])._loadedConfig).toBe(loadedConfig); });
});
it('should load the configuration after an absolute redirect from a wildcard route', () => {
@ -339,9 +340,8 @@ describe('applyRedirects', () => {
const config =
[{path: 'not-found', loadChildren: 'children'}, {path: '**', redirectTo: '/not-found'}];
applyRedirects(<any>'providedInjector', <any>loader, tree('xyz'), config).forEach(r => {
expect((<any>config[0])._loadedConfig).toBe(loadedConfig);
});
applyRedirects(<any>'providedInjector', <any>loader, serializer, tree('xyz'), config)
.forEach(r => { expect((<any>config[0])._loadedConfig).toBe(loadedConfig); });
});
});
@ -388,7 +388,7 @@ describe('applyRedirects', () => {
{path: '', redirectTo: 'a', pathMatch: 'full'}
];
applyRedirects(null, null, tree('b'), config)
applyRedirects(null, null, serializer, tree('b'), config)
.subscribe(
(_) => { throw 'Should not be reached'; },
e => { expect(e.message).toEqual('Cannot match any routes. URL Segment: \'b\''); });
@ -518,7 +518,7 @@ describe('applyRedirects', () => {
]
}];
applyRedirects(null, null, tree('a/(d//aux:e)'), config)
applyRedirects(null, null, serializer, tree('a/(d//aux:e)'), config)
.subscribe(
(_) => { throw 'Should not be reached'; },
e => { expect(e.message).toEqual('Cannot match any routes. URL Segment: \'a\''); });
@ -549,7 +549,7 @@ describe('applyRedirects', () => {
it('should error when no children matching and some url is left', () => {
applyRedirects(
null, null, tree('/a/c'),
null, null, serializer, tree('/a/c'),
[{path: 'a', component: ComponentA, children: [{path: 'b', component: ComponentB}]}])
.subscribe(
(_) => { throw 'Should not be reached'; },
@ -576,10 +576,46 @@ describe('applyRedirects', () => {
'/a/1/b', (t: UrlTree) => { compareTrees(t, tree('a/1/b')); });
});
});
describe('redirecting to named outlets', () => {
it('should work when using absolute redirects', () => {
checkRedirect(
[
{path: 'a/:id', redirectTo: '/b/:id(aux:c/:id)'},
{path: 'b/:id', component: ComponentB},
{path: 'c/:id', component: ComponentC, outlet: 'aux'}
],
'a/1;p=99', (t: UrlTree) => { compareTrees(t, tree('/b/1;p=99(aux:c/1;p=99)')); });
});
it('should work when using absolute redirects (wildcard)', () => {
checkRedirect(
[
{path: '**', redirectTo: '/b(aux:c)'}, {path: 'b', component: ComponentB},
{path: 'c', component: ComponentC, outlet: 'aux'}
],
'a/1', (t: UrlTree) => { compareTrees(t, tree('/b(aux:c)')); });
});
it('should throw when using non-absolute redirects', () => {
applyRedirects(
null, null, serializer, tree('a'),
[
{path: 'a', redirectTo: 'b(aux:c)'},
])
.subscribe(
() => { throw new Error('should not be reached'); },
(e) => {
expect(e.message).toEqual(
'Only absolute redirects can have named outlets. redirectTo: \'b(aux:c)\'');
});
});
});
});
function checkRedirect(config: Routes, url: string, callback: any): void {
applyRedirects(null, null, tree(url), config).subscribe(callback, e => { throw e; });
applyRedirects(null, null, new DefaultUrlSerializer(), tree(url), config)
.subscribe(callback, e => { throw e; });
}
function tree(url: string): UrlTree {
@ -591,6 +627,8 @@ function compareTrees(actual: UrlTree, expected: UrlTree): void {
const error =
`"${serializer.serialize(actual)}" is not equal to "${serializer.serialize(expected)}"`;
compareSegments(actual.root, expected.root, error);
expect(actual.queryParams).toEqual(expected.queryParams);
expect(actual.fragment).toEqual(expected.fragment);
}
function compareSegments(actual: UrlSegmentGroup, expected: UrlSegmentGroup, error: string): void {