chore(router): clang-format

This commit is contained in:
vsavkin 2016-06-21 11:49:42 -07:00
parent 92d8bf9619
commit d1f93072a8
10 changed files with 1514 additions and 1419 deletions

View File

@ -1,200 +1,212 @@
import {DefaultUrlSerializer} from '../src/url_serializer';
import {TreeNode} from '../src/utils/tree';
import {UrlTree, UrlSegment, equalPathsWithParams} from '../src/url_tree';
import {RouterConfig} from '../src/config';
import {applyRedirects} from '../src/apply_redirects'; import {applyRedirects} from '../src/apply_redirects';
import {RouterConfig} from '../src/config';
import {DefaultUrlSerializer} from '../src/url_serializer';
import {UrlSegment, UrlTree, equalPathsWithParams} from '../src/url_tree';
import {TreeNode} from '../src/utils/tree';
describe('applyRedirects', () => { describe('applyRedirects', () => {
it("should return the same url tree when no redirects", () => { it('should return the same url tree when no redirects', () => {
checkRedirect([ checkRedirect(
{path: 'a', component: ComponentA, children: [{path: 'b', component: ComponentB}]} [{path: 'a', component: ComponentA, children: [{path: 'b', component: ComponentB}]}],
], "/a/b", (t:UrlTree) => { '/a/b', (t: UrlTree) => { compareTrees(t, tree('/a/b')); });
compareTrees(t, tree('/a/b'));
});
}); });
it("should add new segments when needed", () => { it('should add new segments when needed', () => {
checkRedirect([ checkRedirect(
{path: 'a/b', redirectTo: 'a/b/c'}, [{path: 'a/b', redirectTo: 'a/b/c'}, {path: '**', component: ComponentC}], '/a/b',
{path: '**', component: ComponentC} (t: UrlTree) => { compareTrees(t, tree('/a/b/c')); });
], "/a/b", (t:UrlTree) => {
compareTrees(t, tree('/a/b/c'));
});
}); });
it("should handle positional parameters", () => { it('should handle positional parameters', () => {
checkRedirect([ checkRedirect(
{path: 'a/:aid/b/:bid', redirectTo: 'newa/:aid/newb/:bid'}, [
{path: '**', component: ComponentC} {path: 'a/:aid/b/:bid', redirectTo: 'newa/:aid/newb/:bid'},
], "/a/1/b/2", (t:UrlTree) => { {path: '**', component: ComponentC}
compareTrees(t, tree('/newa/1/newb/2')); ],
}); '/a/1/b/2', (t: UrlTree) => { compareTrees(t, tree('/newa/1/newb/2')); });
}); });
it("should throw when cannot handle a positional parameter", () => { it('should throw when cannot handle a positional parameter', () => {
applyRedirects(tree("/a/1"), [ applyRedirects(tree('/a/1'), [
{path: 'a/:id', redirectTo: 'a/:other'} {path: 'a/:id', redirectTo: 'a/:other'}
]).subscribe(() => {}, (e) => { ]).subscribe(() => {}, (e) => {
expect(e.message).toEqual("Cannot redirect to 'a/:other'. Cannot find ':other'."); expect(e.message).toEqual('Cannot redirect to \'a/:other\'. Cannot find \':other\'.');
}); });
}); });
it("should pass matrix parameters", () => { it('should pass matrix parameters', () => {
checkRedirect([ checkRedirect(
{path: 'a/:id', redirectTo: 'd/a/:id/e'}, [{path: 'a/:id', redirectTo: 'd/a/:id/e'}, {path: '**', component: ComponentC}],
{path: '**', component: ComponentC} '/a;p1=1/1;p2=2', (t: UrlTree) => { compareTrees(t, tree('/d/a;p1=1/1;p2=2/e')); });
], "/a;p1=1/1;p2=2", (t:UrlTree) => {
compareTrees(t, tree('/d/a;p1=1/1;p2=2/e'));
});
}); });
it("should handle preserve secondary routes", () => { it('should handle preserve secondary routes', () => {
checkRedirect([ checkRedirect(
{path: 'a/:id', redirectTo: 'd/a/:id/e'}, [
{path: 'c/d', component: ComponentA, outlet: 'aux'}, {path: 'a/:id', redirectTo: 'd/a/:id/e'},
{path: '**', component: ComponentC} {path: 'c/d', component: ComponentA, outlet: 'aux'}, {path: '**', component: ComponentC}
], "/a/1(aux:c/d)", (t:UrlTree) => { ],
compareTrees(t, tree('/d/a/1/e(aux:c/d)')); '/a/1(aux:c/d)', (t: UrlTree) => { compareTrees(t, tree('/d/a/1/e(aux:c/d)')); });
});
}); });
it("should redirect secondary routes", () => { it('should redirect secondary routes', () => {
checkRedirect([ checkRedirect(
{path: 'a/:id', component: ComponentA}, [
{path: 'c/d', redirectTo: 'f/c/d/e', outlet: 'aux'}, {path: 'a/:id', component: ComponentA},
{path: '**', component: ComponentC, outlet: 'aux'} {path: 'c/d', redirectTo: 'f/c/d/e', outlet: 'aux'},
], "/a/1(aux:c/d)", (t:UrlTree) => { {path: '**', component: ComponentC, outlet: 'aux'}
compareTrees(t, tree('/a/1(aux:f/c/d/e)')); ],
}); '/a/1(aux:c/d)', (t: UrlTree) => { compareTrees(t, tree('/a/1(aux:f/c/d/e)')); });
}); });
it("should use the configuration of the route redirected to", () => { it('should use the configuration of the route redirected to', () => {
checkRedirect([ checkRedirect(
{path: 'a', component: ComponentA, children: [ [
{path: 'b', component: ComponentB}, {
]}, path: 'a',
{path: 'c', redirectTo: 'a'} component: ComponentA,
], "c/b", (t:UrlTree) => { children: [
compareTrees(t, tree('a/b')); {path: 'b', component: ComponentB},
}); ]
},
{path: 'c', redirectTo: 'a'}
],
'c/b', (t: UrlTree) => { compareTrees(t, tree('a/b')); });
}); });
it("should redirect empty path", () => { it('should redirect empty path', () => {
checkRedirect([ checkRedirect(
{path: 'a', component: ComponentA, children: [ [
{path: 'b', component: ComponentB}, {
]}, path: 'a',
{path: '', redirectTo: 'a'} component: ComponentA,
], "b", (t:UrlTree) => { children: [
compareTrees(t, tree('a/b')); {path: 'b', component: ComponentB},
}); ]
},
{path: '', redirectTo: 'a'}
],
'b', (t: UrlTree) => { compareTrees(t, tree('a/b')); });
}); });
it("should redirect empty path (global redirect)", () => { it('should redirect empty path (global redirect)', () => {
checkRedirect([ checkRedirect(
{path: 'a', component: ComponentA, children: [ [
{path: 'b', component: ComponentB}, {
]}, path: 'a',
{path: '', redirectTo: '/a/b'} component: ComponentA,
], "", (t:UrlTree) => { children: [
compareTrees(t, tree('a/b')); {path: 'b', component: ComponentB},
}); ]
},
{path: '', redirectTo: '/a/b'}
],
'', (t: UrlTree) => { compareTrees(t, tree('a/b')); });
}); });
xit("should support nested redirects", () => { xit('should support nested redirects', () => {
checkRedirect([ checkRedirect(
{path: 'a', component: ComponentA, children: [ [
{path: 'b', component: ComponentB}, {
{path: '', redirectTo: 'b'} path: 'a',
]}, component: ComponentA,
{path: '', redirectTo: 'a'} children: [{path: 'b', component: ComponentB}, {path: '', redirectTo: 'b'}]
], "", (t:UrlTree) => { },
compareTrees(t, tree('a/b')); {path: '', redirectTo: 'a'}
}); ],
'', (t: UrlTree) => { compareTrees(t, tree('a/b')); });
}); });
xit("should support nested redirects (when redirected to an empty path)", () => { xit('should support nested redirects (when redirected to an empty path)', () => {
checkRedirect([ checkRedirect(
{path: '', component: ComponentA, children: [ [
{path: 'b', component: ComponentB}, {
{path: '', redirectTo: 'b'} path: '',
]}, component: ComponentA,
{path: 'a', redirectTo: ''} children: [{path: 'b', component: ComponentB}, {path: '', redirectTo: 'b'}]
], "a", (t:UrlTree) => { },
compareTrees(t, tree('b')); {path: 'a', redirectTo: ''}
}); ],
'a', (t: UrlTree) => { compareTrees(t, tree('b')); });
}); });
xit("should support redirects with both main and aux", () => { xit('should support redirects with both main and aux', () => {
checkRedirect([ checkRedirect(
{path: 'a', children: [ [
{path: 'b', component: ComponentB}, {
{path: '', redirectTo: 'b'}, path: 'a',
children: [
{path: 'b', component: ComponentB}, {path: '', redirectTo: 'b'},
{path: 'c', component: ComponentC, outlet: 'aux'}, {path: 'c', component: ComponentC, outlet: 'aux'},
{path: '', redirectTo: 'c', outlet: 'aux'} {path: '', redirectTo: 'c', outlet: 'aux'}
]}, ]
{path: 'a', redirectTo: ''} },
], "a", (t:UrlTree) => { {path: 'a', redirectTo: ''}
compareTrees(t, tree('a/(b//aux:c)')); ],
}); 'a', (t: UrlTree) => { compareTrees(t, tree('a/(b//aux:c)')); });
}); });
it("should redirect empty path route only when terminal", () => { it('should redirect empty path route only when terminal', () => {
const config = [ const config = [
{path: 'a', component: ComponentA, children: [ {
{path: 'b', component: ComponentB}, path: 'a',
]}, component: ComponentA,
children: [
{path: 'b', component: ComponentB},
]
},
{path: '', redirectTo: 'a', terminal: true} {path: '', redirectTo: 'a', terminal: true}
]; ];
applyRedirects(tree("b"), config).subscribe((_) => { applyRedirects(tree('b'), config)
throw "Should not be reached"; .subscribe(
}, e => { (_) => { throw 'Should not be reached'; },
expect(e.message).toEqual("Cannot match any routes: 'b'"); e => { expect(e.message).toEqual('Cannot match any routes: \'b\''); });
});
}); });
it("should redirect wild cards", () => { it('should redirect wild cards', () => {
checkRedirect([ checkRedirect(
{path: '404', component: ComponentA}, [
{path: '**', redirectTo: '/404'}, {path: '404', component: ComponentA},
], "/a/1(aux:c/d)", (t:UrlTree) => { {path: '**', redirectTo: '/404'},
compareTrees(t, tree('/404')); ],
}); '/a/1(aux:c/d)', (t: UrlTree) => { compareTrees(t, tree('/404')); });
}); });
it("should support global redirects", () => { it('should support global redirects', () => {
checkRedirect([ checkRedirect(
{path: 'a', component: ComponentA, children: [ [
{path: 'b/:id', redirectTo: '/global/:id'} {
]}, path: 'a',
{path: '**', component: ComponentC} component: ComponentA,
], "/a/b/1", (t:UrlTree) => { children: [{path: 'b/:id', redirectTo: '/global/:id'}]
compareTrees(t, tree('/global/1')); },
}); {path: '**', component: ComponentC}
],
'/a/b/1', (t: UrlTree) => { compareTrees(t, tree('/global/1')); });
}); });
}); });
function checkRedirect(config: RouterConfig, url: string, callback: any): void { function checkRedirect(config: RouterConfig, url: string, callback: any): void {
applyRedirects(tree(url), config).subscribe(callback, e => { applyRedirects(tree(url), config).subscribe(callback, e => { throw e; });
throw e;
});
} }
function tree(url: string): UrlTree { function tree(url: string): UrlTree {
return new DefaultUrlSerializer().parse(url); return new DefaultUrlSerializer().parse(url);
} }
function compareTrees(actual: UrlTree, expected: UrlTree): void{ function compareTrees(actual: UrlTree, expected: UrlTree): void {
const serializer = new DefaultUrlSerializer(); const serializer = new DefaultUrlSerializer();
const error = `"${serializer.serialize(actual)}" is not equal to "${serializer.serialize(expected)}"`; const error =
`"${serializer.serialize(actual)}" is not equal to "${serializer.serialize(expected)}"`;
compareSegments(actual.root, expected.root, error); compareSegments(actual.root, expected.root, error);
} }
function compareSegments(actual: UrlSegment, expected: UrlSegment, error: string): void{ function compareSegments(actual: UrlSegment, expected: UrlSegment, error: string): void {
expect(actual).toBeDefined(error); expect(actual).toBeDefined(error);
expect(equalPathsWithParams(actual.pathsWithParams, expected.pathsWithParams)).toEqual(true, error); expect(equalPathsWithParams(actual.pathsWithParams, expected.pathsWithParams))
.toEqual(true, error);
expect(Object.keys(actual.children).length).toEqual(Object.keys(expected.children).length, error); expect(Object.keys(actual.children).length).toEqual(Object.keys(expected.children).length, error);

View File

@ -1,63 +1,46 @@
import {validateConfig} from '../src/config'; import {validateConfig} from '../src/config';
describe('config', () => { describe('config', () => {
describe("validateConfig", () => { describe('validateConfig', () => {
it("should not throw when no errors", () => { it('should not throw when no errors', () => {
validateConfig([ validateConfig([{path: '', redirectTo: 'b'}, {path: 'b', component: ComponentA}]);
{path: '', redirectTo: 'b'},
{path: 'b', component: ComponentA}
]);
}); });
it("should throw when redirectTo and children are used together", () => { it('should throw when redirectTo and children are used together', () => {
expect(() => { expect(() => {
validateConfig([ validateConfig(
{ [{path: 'a', redirectTo: 'b', children: [{path: 'b', component: ComponentA}]}]);
path: 'a', redirectTo: 'b', children: [ })
{path: 'b', component: ComponentA} .toThrowError(
] `Invalid configuration of route 'a': redirectTo and children cannot be used together`);
}
]);
}).toThrowError(`Invalid configuration of route 'a': redirectTo and children cannot be used together`);
}); });
it("should throw when component and redirectTo are used together", () => { it('should throw when component and redirectTo are used together', () => {
expect(() => { expect(() => { validateConfig([{path: 'a', component: ComponentA, redirectTo: 'b'}]); })
validateConfig([ .toThrowError(
{path: 'a', component: ComponentA, redirectTo: 'b'} `Invalid configuration of route 'a': redirectTo and component cannot be used together`);
]);
}).toThrowError(`Invalid configuration of route 'a': redirectTo and component cannot be used together`);
}); });
it("should throw when path is missing", () => { it('should throw when path is missing', () => {
expect(() => { expect(() => {
validateConfig([ validateConfig([{component: '', redirectTo: 'b'}]);
{component: '', redirectTo: 'b'}
]);
}).toThrowError(`Invalid route configuration: routes must have path specified`); }).toThrowError(`Invalid route configuration: routes must have path specified`);
}); });
it("should throw when none of component and children or direct are missing", () => { it('should throw when none of component and children or direct are missing', () => {
expect(() => { expect(() => { validateConfig([{path: 'a'}]); })
validateConfig([ .toThrowError(
{path: 'a'} `Invalid configuration of route 'a': component, redirectTo, children must be provided`);
]);
}).toThrowError(`Invalid configuration of route 'a': component, redirectTo, children must be provided`);
}); });
it("should throw when path starts with a slash", () => { it('should throw when path starts with a slash', () => {
expect(() => { expect(() => {
validateConfig([ validateConfig([<any>{path: '/a', componenta: '', redirectTo: 'b'}]);
<any>{path: '/a', componenta: '', redirectTo: 'b'}
]);
}).toThrowError(`Invalid route configuration of route '/a': path cannot start with a slash`); }).toThrowError(`Invalid route configuration of route '/a': path cannot start with a slash`);
}); });
}); });
}); });
class ComponentA { class ComponentA {}
} class ComponentB {}
class ComponentB { class ComponentC {}
}
class ComponentC {
}

View File

@ -1,21 +1,26 @@
import {DefaultUrlSerializer} from '../src/url_serializer'; import {RouterConfig} from '../src/config';
import {UrlTree, UrlSegment} from '../src/url_tree';
import {TreeNode} from '../src/utils/tree';
import {Params, PRIMARY_OUTLET} from '../src/shared';
import {ActivatedRoute, RouterState, RouterStateSnapshot, createEmptyState, advanceActivatedRoute} from '../src/router_state';
import {createRouterState} from '../src/create_router_state'; import {createRouterState} from '../src/create_router_state';
import {recognize} from '../src/recognize'; import {recognize} from '../src/recognize';
import {RouterConfig} from '../src/config'; import {ActivatedRoute, RouterState, RouterStateSnapshot, advanceActivatedRoute, createEmptyState} from '../src/router_state';
import {PRIMARY_OUTLET, Params} from '../src/shared';
import {DefaultUrlSerializer} from '../src/url_serializer';
import {UrlSegment, UrlTree} from '../src/url_tree';
import {TreeNode} from '../src/utils/tree';
describe('create router state', () => { describe('create router state', () => {
const emptyState = () => createEmptyState(new UrlTree(new UrlSegment([], {}), {}, null), RootComponent); const emptyState = () =>
createEmptyState(new UrlTree(new UrlSegment([], {}), {}, null), RootComponent);
it('should work create new state', () => { it('should work create new state', () => {
const state = createRouterState(createState([ const state = createRouterState(
{path: 'a', component: ComponentA}, createState(
{path: 'b', component: ComponentB, outlet: 'left'}, [
{path: 'c', component: ComponentC, outlet: 'right'} {path: 'a', component: ComponentA},
], "a(left:b//right:c)"), emptyState()); {path: 'b', component: ComponentB, outlet: 'left'},
{path: 'c', component: ComponentC, outlet: 'right'}
],
'a(left:b//right:c)'),
emptyState());
checkActivatedRoute(state.root, RootComponent); checkActivatedRoute(state.root, RootComponent);
@ -27,14 +32,13 @@ describe('create router state', () => {
it('should reuse existing nodes when it can', () => { it('should reuse existing nodes when it can', () => {
const config = [ const config = [
{path: 'a', component: ComponentA}, {path: 'a', component: ComponentA}, {path: 'b', component: ComponentB, outlet: 'left'},
{path: 'b', component: ComponentB, outlet: 'left'},
{path: 'c', component: ComponentC, outlet: 'left'} {path: 'c', component: ComponentC, outlet: 'left'}
]; ];
const prevState = createRouterState(createState(config, "a(left:b)"), emptyState()); const prevState = createRouterState(createState(config, 'a(left:b)'), emptyState());
advanceState(prevState); advanceState(prevState);
const state = createRouterState(createState(config, "a(left:c)"), prevState); const state = createRouterState(createState(config, 'a(left:c)'), prevState);
expect(prevState.root).toBe(state.root); expect(prevState.root).toBe(state.root);
const prevC = prevState.children(prevState.root); const prevC = prevState.children(prevState.root);
@ -46,17 +50,17 @@ describe('create router state', () => {
}); });
it('should handle componentless routes', () => { it('should handle componentless routes', () => {
const config = [ const config = [{
{ path: 'a/:id', children: [ path: 'a/:id',
{ path: 'b', component: ComponentA }, children: [
{ path: 'c', component: ComponentB, outlet: 'right' } {path: 'b', component: ComponentA}, {path: 'c', component: ComponentB, outlet: 'right'}
] } ]
]; }];
const prevState = createRouterState(createState(config, "a/1;p=11/(b//right:c)"), emptyState()); const prevState = createRouterState(createState(config, 'a/1;p=11/(b//right:c)'), emptyState());
advanceState(prevState); advanceState(prevState);
const state = createRouterState(createState(config, "a/2;p=22/(b//right:c)"), prevState); const state = createRouterState(createState(config, 'a/2;p=22/(b//right:c)'), prevState);
expect(prevState.root).toBe(state.root); expect(prevState.root).toBe(state.root);
const prevP = prevState.firstChild(prevState.root); const prevP = prevState.firstChild(prevState.root);
@ -87,7 +91,8 @@ function createState(config: RouterConfig, url: string): RouterStateSnapshot {
return res; return res;
} }
function checkActivatedRoute(actual: ActivatedRoute, cmp: Function, outlet: string = PRIMARY_OUTLET):void { function checkActivatedRoute(
actual: ActivatedRoute, cmp: Function, outlet: string = PRIMARY_OUTLET): void {
if (actual === null) { if (actual === null) {
expect(actual).toBeDefined(); expect(actual).toBeDefined();
} else { } else {

View File

@ -1,182 +1,191 @@
import {DefaultUrlSerializer} from '../src/url_serializer'; import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {UrlTree, UrlPathWithParams, UrlSegment} from '../src/url_tree';
import {createUrlTree} from '../src/create_url_tree';
import {ActivatedRoute, ActivatedRouteSnapshot, advanceActivatedRoute} from '../src/router_state'; import {ActivatedRoute, ActivatedRouteSnapshot, advanceActivatedRoute} from '../src/router_state';
import {PRIMARY_OUTLET, Params} from '../src/shared'; import {PRIMARY_OUTLET, Params} from '../src/shared';
import {createUrlTree} from '../src/create_url_tree'; import {DefaultUrlSerializer} from '../src/url_serializer';
import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {UrlPathWithParams, UrlSegment, UrlTree} from '../src/url_tree';
describe('createUrlTree', () => { describe('createUrlTree', () => {
const serializer = new DefaultUrlSerializer(); const serializer = new DefaultUrlSerializer();
it("should navigate to the root", () => { it('should navigate to the root', () => {
const p = serializer.parse("/"); const p = serializer.parse('/');
const t = createRoot(p, ["/"]); const t = createRoot(p, ['/']);
expect(serializer.serialize(t)).toEqual("/"); expect(serializer.serialize(t)).toEqual('/');
}); });
it("should support nested segments", () => { it('should support nested segments', () => {
const p = serializer.parse("/a/b"); const p = serializer.parse('/a/b');
const t = createRoot(p, ["/one", 11, "two", 22]); const t = createRoot(p, ['/one', 11, 'two', 22]);
expect(serializer.serialize(t)).toEqual("/one/11/two/22"); expect(serializer.serialize(t)).toEqual('/one/11/two/22');
}); });
it("should stringify positional parameters", () => { it('should stringify positional parameters', () => {
const p = serializer.parse("/a/b"); const p = serializer.parse('/a/b');
const t = createRoot(p, ["/one", 11]); const t = createRoot(p, ['/one', 11]);
const params = t.root.children[PRIMARY_OUTLET].pathsWithParams; const params = t.root.children[PRIMARY_OUTLET].pathsWithParams;
expect(params[0].path).toEqual("one"); expect(params[0].path).toEqual('one');
expect(params[1].path).toEqual("11"); expect(params[1].path).toEqual('11');
}); });
it("should preserve secondary segments", () => { it('should preserve secondary segments', () => {
const p = serializer.parse("/a/11/b(right:c)"); const p = serializer.parse('/a/11/b(right:c)');
const t = createRoot(p, ["/a", 11, 'd']); const t = createRoot(p, ['/a', 11, 'd']);
expect(serializer.serialize(t)).toEqual("/a/11/d(right:c)"); expect(serializer.serialize(t)).toEqual('/a/11/d(right:c)');
}); });
it("should support updating secondary segments", () => { it('should support updating secondary segments', () => {
const p = serializer.parse("/a(right:b)"); const p = serializer.parse('/a(right:b)');
const t = createRoot(p, ["right:c", 11, 'd']); const t = createRoot(p, ['right:c', 11, 'd']);
expect(serializer.serialize(t)).toEqual("/a(right:c/11/d)"); expect(serializer.serialize(t)).toEqual('/a(right:c/11/d)');
}); });
it("should support updating secondary segments (nested case)", () => { it('should support updating secondary segments (nested case)', () => {
const p = serializer.parse("/a/(b//right:c)"); const p = serializer.parse('/a/(b//right:c)');
const t = createRoot(p, ["a", "right:d", 11, 'e']); const t = createRoot(p, ['a', 'right:d', 11, 'e']);
expect(serializer.serialize(t)).toEqual("/a/(b//right:d/11/e)"); expect(serializer.serialize(t)).toEqual('/a/(b//right:d/11/e)');
}); });
it('should update matrix parameters', () => { it('should update matrix parameters', () => {
const p = serializer.parse("/a;pp=11"); const p = serializer.parse('/a;pp=11');
const t = createRoot(p, ["/a", {pp: 22, dd: 33}]); const t = createRoot(p, ['/a', {pp: 22, dd: 33}]);
expect(serializer.serialize(t)).toEqual("/a;pp=22;dd=33"); expect(serializer.serialize(t)).toEqual('/a;pp=22;dd=33');
}); });
it('should create matrix parameters', () => { it('should create matrix parameters', () => {
const p = serializer.parse("/a"); const p = serializer.parse('/a');
const t = createRoot(p, ["/a", {pp: 22, dd: 33}]); const t = createRoot(p, ['/a', {pp: 22, dd: 33}]);
expect(serializer.serialize(t)).toEqual("/a;pp=22;dd=33"); expect(serializer.serialize(t)).toEqual('/a;pp=22;dd=33');
}); });
it('should create matrix parameters together with other segments', () => { it('should create matrix parameters together with other segments', () => {
const p = serializer.parse("/a"); const p = serializer.parse('/a');
const t = createRoot(p, ["/a", "/b", {aa: 22, bb: 33}]); const t = createRoot(p, ['/a', '/b', {aa: 22, bb: 33}]);
expect(serializer.serialize(t)).toEqual("/a/b;aa=22;bb=33"); expect(serializer.serialize(t)).toEqual('/a/b;aa=22;bb=33');
}); });
describe("relative navigation", () => { describe('relative navigation', () => {
it("should work", () => { it('should work', () => {
const p = serializer.parse("/a/(c//left:cp)(left:ap)"); const p = serializer.parse('/a/(c//left:cp)(left:ap)');
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ["c2"]); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['c2']);
expect(serializer.serialize(t)).toEqual("/a/(c2//left:cp)(left:ap)"); expect(serializer.serialize(t)).toEqual('/a/(c2//left:cp)(left:ap)');
}); });
it("should work when the first command starts with a ./", () => { it('should work when the first command starts with a ./', () => {
const p = serializer.parse("/a/(c//left:cp)(left:ap)"); const p = serializer.parse('/a/(c//left:cp)(left:ap)');
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ["./c2"]); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['./c2']);
expect(serializer.serialize(t)).toEqual("/a/(c2//left:cp)(left:ap)"); expect(serializer.serialize(t)).toEqual('/a/(c2//left:cp)(left:ap)');
}); });
it("should work when the first command is ./)", () => { it('should work when the first command is ./)', () => {
const p = serializer.parse("/a/(c//left:cp)(left:ap)"); const p = serializer.parse('/a/(c//left:cp)(left:ap)');
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ["./", "c2"]); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['./', 'c2']);
expect(serializer.serialize(t)).toEqual("/a/(c2//left:cp)(left:ap)"); expect(serializer.serialize(t)).toEqual('/a/(c2//left:cp)(left:ap)');
}); });
it("should work when given params", () => { it('should work when given params', () => {
const p = serializer.parse("/a/(c//left:cp)(left:ap)"); const p = serializer.parse('/a/(c//left:cp)(left:ap)');
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, [{'x': 99}]); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, [{'x': 99}]);
expect(serializer.serialize(t)).toEqual("/a/(c;x=99//left:cp)(left:ap)"); expect(serializer.serialize(t)).toEqual('/a/(c;x=99//left:cp)(left:ap)');
}); });
it("should work when index > 0", () => { it('should work when index > 0', () => {
const p = serializer.parse("/a/c"); const p = serializer.parse('/a/c');
const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ["c2"]); const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ['c2']);
expect(serializer.serialize(t)).toEqual("/a/c/c2"); expect(serializer.serialize(t)).toEqual('/a/c/c2');
}); });
it("should support going to a parent (within a segment)", () => { it('should support going to a parent (within a segment)', () => {
const p = serializer.parse("/a/c"); const p = serializer.parse('/a/c');
const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ["../c2"]); const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ['../c2']);
expect(serializer.serialize(t)).toEqual("/a/c2"); expect(serializer.serialize(t)).toEqual('/a/c2');
}); });
it("should work when given ../", () => { it('should work when given ../', () => {
const p = serializer.parse("/a/c"); const p = serializer.parse('/a/c');
const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ["../", "c2"]); const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ['../', 'c2']);
expect(serializer.serialize(t)).toEqual("/a/c2"); expect(serializer.serialize(t)).toEqual('/a/c2');
}); });
it("should support setting matrix params", () => { it('should support setting matrix params', () => {
const p = serializer.parse("/a/(c//left:cp)(left:ap)"); const p = serializer.parse('/a/(c//left:cp)(left:ap)');
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../', {x: 5}]); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../', {x: 5}]);
expect(serializer.serialize(t)).toEqual("/a;x=5(left:ap)"); expect(serializer.serialize(t)).toEqual('/a;x=5(left:ap)');
}); });
xit("should support going to a parent (across segments)", () => { xit('should support going to a parent (across segments)', () => {
const p = serializer.parse("/q/(a/(c//left:cp)//left:qp)(left:ap)"); const p = serializer.parse('/q/(a/(c//left:cp)//left:qp)(left:ap)');
const t = create(p.root.children[PRIMARY_OUTLET].children[PRIMARY_OUTLET], 0, p, ['../../q2']); const t =
expect(serializer.serialize(t)).toEqual("/q2(left:ap)"); create(p.root.children[PRIMARY_OUTLET].children[PRIMARY_OUTLET], 0, p, ['../../q2']);
expect(serializer.serialize(t)).toEqual('/q2(left:ap)');
}); });
xit("should navigate to the root", () => { xit('should navigate to the root', () => {
const p = serializer.parse("/a/c"); const p = serializer.parse('/a/c');
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../']); const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../']);
expect(serializer.serialize(t)).toEqual(""); expect(serializer.serialize(t)).toEqual('');
}); });
it("should throw when too many ..", () => { it('should throw when too many ..', () => {
const p = serializer.parse("/a/(c//left:cp)(left:ap)"); const p = serializer.parse('/a/(c//left:cp)(left:ap)');
expect(() => create(p.root.children[PRIMARY_OUTLET], 0, p, ['../../'])).toThrowError("Invalid number of '../'"); expect(() => create(p.root.children[PRIMARY_OUTLET], 0, p, ['../../']))
.toThrowError('Invalid number of \'../\'');
}); });
}); });
it("should set query params", () => { it('should set query params', () => {
const p = serializer.parse("/"); const p = serializer.parse('/');
const t = createRoot(p, [], {a: 'hey'}); const t = createRoot(p, [], {a: 'hey'});
expect(t.queryParams).toEqual({a: 'hey'}); expect(t.queryParams).toEqual({a: 'hey'});
}); });
it("should stringify query params", () => { it('should stringify query params', () => {
const p = serializer.parse("/"); const p = serializer.parse('/');
const t = createRoot(p, [], <any>{a: 1}); const t = createRoot(p, [], <any>{a: 1});
expect(t.queryParams).toEqual({a: '1'}); expect(t.queryParams).toEqual({a: '1'});
}); });
it("should reuse old query params when given undefined", () => { it('should reuse old query params when given undefined', () => {
const p = serializer.parse("/?a=1"); const p = serializer.parse('/?a=1');
const t = createRoot(p, [], undefined); const t = createRoot(p, [], undefined);
expect(t.queryParams).toEqual({a: '1'}); expect(t.queryParams).toEqual({a: '1'});
}); });
it("should set fragment", () => { it('should set fragment', () => {
const p = serializer.parse("/"); const p = serializer.parse('/');
const t = createRoot(p, [], {}, "fragment"); const t = createRoot(p, [], {}, 'fragment');
expect(t.fragment).toEqual("fragment"); expect(t.fragment).toEqual('fragment');
}); });
it("should reused old fragment when given undefined", () => { it('should reused old fragment when given undefined', () => {
const p = serializer.parse("/#fragment"); const p = serializer.parse('/#fragment');
const t = createRoot(p, [], undefined, undefined); const t = createRoot(p, [], undefined, undefined);
expect(t.fragment).toEqual("fragment"); expect(t.fragment).toEqual('fragment');
}); });
}); });
function createRoot(tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) { function createRoot(tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) {
const s = new ActivatedRouteSnapshot([], <any>{}, PRIMARY_OUTLET, "someComponent", null, tree.root, -1); const s =
const a = new ActivatedRoute(new BehaviorSubject(null), new BehaviorSubject(null), PRIMARY_OUTLET, "someComponent", s); new ActivatedRouteSnapshot([], <any>{}, PRIMARY_OUTLET, 'someComponent', null, tree.root, -1);
const a = new ActivatedRoute(
new BehaviorSubject(null), new BehaviorSubject(null), PRIMARY_OUTLET, 'someComponent', s);
advanceActivatedRoute(a); advanceActivatedRoute(a);
return createUrlTree(a, tree, commands, queryParams, fragment); return createUrlTree(a, tree, commands, queryParams, fragment);
} }
function create(segment: UrlSegment, startIndex: number, tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) { function create(
segment: UrlSegment, startIndex: number, tree: UrlTree, commands: any[], queryParams?: Params,
fragment?: string) {
if (!segment) { if (!segment) {
expect(segment).toBeDefined(); expect(segment).toBeDefined();
} }
const s = new ActivatedRouteSnapshot([], <any>{}, PRIMARY_OUTLET, "someComponent", null, <any>segment, startIndex); const s = new ActivatedRouteSnapshot(
const a = new ActivatedRoute(new BehaviorSubject(null), new BehaviorSubject(null), PRIMARY_OUTLET, "someComponent", s); [], <any>{}, PRIMARY_OUTLET, 'someComponent', null, <any>segment, startIndex);
const a = new ActivatedRoute(
new BehaviorSubject(null), new BehaviorSubject(null), PRIMARY_OUTLET, 'someComponent', s);
advanceActivatedRoute(a); advanceActivatedRoute(a);
return createUrlTree(a, tree, commands, queryParams, fragment); return createUrlTree(a, tree, commands, queryParams, fragment);
} }

View File

@ -1,412 +1,433 @@
import {DefaultUrlSerializer} from '../src/url_serializer';
import {UrlTree} from '../src/url_tree';
import {Params, PRIMARY_OUTLET} from '../src/shared';
import {ActivatedRouteSnapshot, RouterStateSnapshot} from '../src/router_state';
import {RouterConfig} from '../src/config'; import {RouterConfig} from '../src/config';
import {recognize} from '../src/recognize'; import {recognize} from '../src/recognize';
import {ActivatedRouteSnapshot, RouterStateSnapshot} from '../src/router_state';
import {PRIMARY_OUTLET, Params} from '../src/shared';
import {DefaultUrlSerializer} from '../src/url_serializer';
import {UrlTree} from '../src/url_tree';
describe('recognize', () => { describe('recognize', () => {
it('should work', () => { it('should work', () => {
checkRecognize([ checkRecognize([{path: 'a', component: ComponentA}], 'a', (s: RouterStateSnapshot) => {
{ checkActivatedRoute(s.root, '', {}, RootComponent);
path: 'a', component: ComponentA checkActivatedRoute(s.firstChild(s.root), 'a', {}, ComponentA);
}
], "a", (s:RouterStateSnapshot) => {
checkActivatedRoute(s.root, "", {}, RootComponent);
checkActivatedRoute(s.firstChild(s.root), "a", {}, ComponentA);
}); });
}); });
it('should support secondary routes', () => { it('should support secondary routes', () => {
checkRecognize([ checkRecognize(
{ path: 'a', component: ComponentA }, [
{ path: 'b', component: ComponentB, outlet: 'left' }, {path: 'a', component: ComponentA}, {path: 'b', component: ComponentB, outlet: 'left'},
{ path: 'c', component: ComponentC, outlet: 'right' } {path: 'c', component: ComponentC, outlet: 'right'}
], "a(left:b//right:c)", (s:RouterStateSnapshot) => { ],
const c = s.children(s.root); 'a(left:b//right:c)', (s: RouterStateSnapshot) => {
checkActivatedRoute(c[0], "a", {}, ComponentA); const c = s.children(s.root);
checkActivatedRoute(c[1], "b", {}, ComponentB, 'left'); checkActivatedRoute(c[0], 'a', {}, ComponentA);
checkActivatedRoute(c[2], "c", {}, ComponentC, 'right'); checkActivatedRoute(c[1], 'b', {}, ComponentB, 'left');
}); checkActivatedRoute(c[2], 'c', {}, ComponentC, 'right');
});
}); });
it('should set url segment and index properly', () => { it('should set url segment and index properly', () => {
const url = tree("a(left:b//right:c)"); const url = tree('a(left:b//right:c)');
recognize(RootComponent, [ recognize(
{ path: 'a', component: ComponentA }, RootComponent,
{ path: 'b', component: ComponentB, outlet: 'left' }, [
{ path: 'c', component: ComponentC, outlet: 'right' } {path: 'a', component: ComponentA}, {path: 'b', component: ComponentB, outlet: 'left'},
], url, "a(left:b//right:c)").subscribe((s) => { {path: 'c', component: ComponentC, outlet: 'right'}
expect(s.root._urlSegment).toBe(url.root); ],
expect(s.root._lastPathIndex).toBe(-1); url, 'a(left:b//right:c)')
.subscribe((s) => {
expect(s.root._urlSegment).toBe(url.root);
expect(s.root._lastPathIndex).toBe(-1);
const c = s.children(s.root); const c = s.children(s.root);
expect(c[0]._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]); expect(c[0]._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
expect(c[0]._lastPathIndex).toBe(0); expect(c[0]._lastPathIndex).toBe(0);
expect(c[1]._urlSegment).toBe(url.root.children["left"]); expect(c[1]._urlSegment).toBe(url.root.children['left']);
expect(c[1]._lastPathIndex).toBe(0); expect(c[1]._lastPathIndex).toBe(0);
expect(c[2]._urlSegment).toBe(url.root.children["right"]); expect(c[2]._urlSegment).toBe(url.root.children['right']);
expect(c[2]._lastPathIndex).toBe(0); expect(c[2]._lastPathIndex).toBe(0);
}); });
}); });
it('should set url segment and index properly (nested case)', () => { it('should set url segment and index properly (nested case)', () => {
const url = tree("a/b/c"); const url = tree('a/b/c');
recognize(RootComponent, [ recognize(
{ path: 'a/b', component: ComponentA, children: [ RootComponent,
{path: 'c', component: ComponentC} [
] }, {path: 'a/b', component: ComponentA, children: [{path: 'c', component: ComponentC}]},
], url, "a/b/c").subscribe((s:RouterStateSnapshot) => { ],
expect(s.root._urlSegment).toBe(url.root); url, 'a/b/c')
expect(s.root._lastPathIndex).toBe(-1); .subscribe((s: RouterStateSnapshot) => {
expect(s.root._urlSegment).toBe(url.root);
expect(s.root._lastPathIndex).toBe(-1);
const compA = s.firstChild(s.root); const compA = s.firstChild(s.root);
expect(compA._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]); expect(compA._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
expect(compA._lastPathIndex).toBe(1); expect(compA._lastPathIndex).toBe(1);
const compC = s.firstChild(<any>compA); const compC = s.firstChild(<any>compA);
expect(compC._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]); expect(compC._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
expect(compC._lastPathIndex).toBe(2); expect(compC._lastPathIndex).toBe(2);
}); });
}); });
it('should match routes in the depth first order', () => { it('should match routes in the depth first order', () => {
checkRecognize([ checkRecognize(
{path: 'a', component: ComponentA, children: [{path: ':id', component: ComponentB}]}, [
{path: 'a/:id', component: ComponentC} {path: 'a', component: ComponentA, children: [{path: ':id', component: ComponentB}]},
], "a/paramA", (s:RouterStateSnapshot) => { {path: 'a/:id', component: ComponentC}
checkActivatedRoute(s.root, "", {}, RootComponent); ],
checkActivatedRoute(s.firstChild(s.root), "a", {}, ComponentA); 'a/paramA', (s: RouterStateSnapshot) => {
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "paramA", {id: 'paramA'}, ComponentB); checkActivatedRoute(s.root, '', {}, RootComponent);
}); checkActivatedRoute(s.firstChild(s.root), 'a', {}, ComponentA);
checkActivatedRoute(
s.firstChild(<any>s.firstChild(s.root)), 'paramA', {id: 'paramA'}, ComponentB);
});
checkRecognize([ checkRecognize(
{path: 'a', component: ComponentA}, [{path: 'a', component: ComponentA}, {path: 'a/:id', component: ComponentC}], 'a/paramA',
{path: 'a/:id', component: ComponentC} (s: RouterStateSnapshot) => {
], "a/paramA", (s:RouterStateSnapshot) => { checkActivatedRoute(s.root, '', {}, RootComponent);
checkActivatedRoute(s.root, "", {}, RootComponent); checkActivatedRoute(s.firstChild(s.root), 'a/paramA', {id: 'paramA'}, ComponentC);
checkActivatedRoute(s.firstChild(s.root), "a/paramA", {id: 'paramA'}, ComponentC); });
});
}); });
it('should use outlet name when matching secondary routes', () => { it('should use outlet name when matching secondary routes', () => {
checkRecognize([ checkRecognize(
{ path: 'a', component: ComponentA }, [
{ path: 'b', component: ComponentB, outlet: 'left' }, {path: 'a', component: ComponentA}, {path: 'b', component: ComponentB, outlet: 'left'},
{ path: 'b', component: ComponentC, outlet: 'right' } {path: 'b', component: ComponentC, outlet: 'right'}
], "a(right:b)", (s:RouterStateSnapshot) => { ],
const c = s.children(s.root); 'a(right:b)', (s: RouterStateSnapshot) => {
checkActivatedRoute(c[0], "a", {}, ComponentA); const c = s.children(s.root);
checkActivatedRoute(c[1], "b", {}, ComponentC, 'right'); checkActivatedRoute(c[0], 'a', {}, ComponentA);
}); checkActivatedRoute(c[1], 'b', {}, ComponentC, 'right');
});
}); });
xit('should handle nested secondary routes', () => { xit('should handle nested secondary routes', () => {
checkRecognize([ checkRecognize(
{ path: 'a', component: ComponentA }, [
{ path: 'b', component: ComponentB, outlet: 'left' }, {path: 'a', component: ComponentA}, {path: 'b', component: ComponentB, outlet: 'left'},
{ path: 'c', component: ComponentC, outlet: 'right' } {path: 'c', component: ComponentC, outlet: 'right'}
], "a(left:b(right:c))", (s:RouterStateSnapshot) => { ],
const c = s.children(s.root); 'a(left:b(right:c))', (s: RouterStateSnapshot) => {
checkActivatedRoute(c[0], "a", {}, ComponentA); const c = s.children(s.root);
checkActivatedRoute(c[1], "b", {}, ComponentB, 'left'); checkActivatedRoute(c[0], 'a', {}, ComponentA);
checkActivatedRoute(c[2], "c", {}, ComponentC, 'right'); checkActivatedRoute(c[1], 'b', {}, ComponentB, 'left');
}); checkActivatedRoute(c[2], 'c', {}, ComponentC, 'right');
});
}); });
it('should handle non top-level secondary routes', () => { it('should handle non top-level secondary routes', () => {
checkRecognize([ checkRecognize(
{ path: 'a', component: ComponentA, children: [ [
{ path: 'b', component: ComponentB }, {
{ path: 'c', component: ComponentC, outlet: 'left' } path: 'a',
] }, component: ComponentA,
], "a/(b//left:c)", (s:RouterStateSnapshot) => { children: [
const c = s.children(<any>s.firstChild(s.root)); {path: 'b', component: ComponentB},
checkActivatedRoute(c[0], "b", {}, ComponentB, PRIMARY_OUTLET); {path: 'c', component: ComponentC, outlet: 'left'}
checkActivatedRoute(c[1], "c", {}, ComponentC, 'left'); ]
}); },
],
'a/(b//left:c)', (s: RouterStateSnapshot) => {
const c = s.children(<any>s.firstChild(s.root));
checkActivatedRoute(c[0], 'b', {}, ComponentB, PRIMARY_OUTLET);
checkActivatedRoute(c[1], 'c', {}, ComponentC, 'left');
});
}); });
it('should sort routes by outlet name', () => { it('should sort routes by outlet name', () => {
checkRecognize([ checkRecognize(
{ path: 'a', component: ComponentA }, [
{ path: 'c', component: ComponentC, outlet: 'c' }, {path: 'a', component: ComponentA}, {path: 'c', component: ComponentC, outlet: 'c'},
{ path: 'b', component: ComponentB, outlet: 'b' } {path: 'b', component: ComponentB, outlet: 'b'}
], "a(c:c//b:b)", (s:RouterStateSnapshot) => { ],
const c = s.children(s.root); 'a(c:c//b:b)', (s: RouterStateSnapshot) => {
checkActivatedRoute(c[0], "a", {}, ComponentA); const c = s.children(s.root);
checkActivatedRoute(c[1], "b", {}, ComponentB, 'b'); checkActivatedRoute(c[0], 'a', {}, ComponentA);
checkActivatedRoute(c[2], "c", {}, ComponentC, 'c'); checkActivatedRoute(c[1], 'b', {}, ComponentB, 'b');
}); checkActivatedRoute(c[2], 'c', {}, ComponentC, 'c');
});
}); });
it('should support matrix parameters', () => { it('should support matrix parameters', () => {
checkRecognize([ checkRecognize(
{ [
path: 'a', component: ComponentA, children: [ {path: 'a', component: ComponentA, children: [{path: 'b', component: ComponentB}]},
{ path: 'b', component: ComponentB } {path: 'c', component: ComponentC, outlet: 'left'}
] ],
}, 'a;a1=11;a2=22/b;b1=111;b2=222(left:c;c1=1111;c2=2222)', (s: RouterStateSnapshot) => {
{ path: 'c', component: ComponentC, outlet: 'left' } const c = s.children(s.root);
], "a;a1=11;a2=22/b;b1=111;b2=222(left:c;c1=1111;c2=2222)", (s:RouterStateSnapshot) => { checkActivatedRoute(c[0], 'a', {a1: '11', a2: '22'}, ComponentA);
const c = s.children(s.root); checkActivatedRoute(s.firstChild(<any>c[0]), 'b', {b1: '111', b2: '222'}, ComponentB);
checkActivatedRoute(c[0], "a", {a1: '11', a2: '22'}, ComponentA); checkActivatedRoute(c[1], 'c', {c1: '1111', c2: '2222'}, ComponentC, 'left');
checkActivatedRoute(s.firstChild(<any>c[0]), "b", {b1: '111', b2: '222'}, ComponentB); });
checkActivatedRoute(c[1], "c", {c1: '1111', c2: '2222'}, ComponentC, 'left');
});
}); });
describe("matching empty url", () => { describe('matching empty url', () => {
it("should support root index routes", () => { it('should support root index routes', () => {
recognize(RootComponent, [ recognize(RootComponent, [{path: '', component: ComponentA}], tree(''), '')
{path: '', component: ComponentA} .forEach((s: RouterStateSnapshot) => {
], tree(""), "").forEach((s:RouterStateSnapshot) => { checkActivatedRoute(s.firstChild(s.root), '', {}, ComponentA);
checkActivatedRoute(s.firstChild(s.root), "", {}, ComponentA); });
});
}); });
it("should support nested root index routes", () => { it('should support nested root index routes', () => {
recognize(RootComponent, [ recognize(
{path: '', component: ComponentA, children: [{path: '', component: ComponentB}]} RootComponent,
], tree(""), "").forEach((s:RouterStateSnapshot) => { [{path: '', component: ComponentA, children: [{path: '', component: ComponentB}]}],
checkActivatedRoute(s.firstChild(s.root), "", {}, ComponentA); tree(''), '')
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {}, ComponentB); .forEach((s: RouterStateSnapshot) => {
}); checkActivatedRoute(s.firstChild(s.root), '', {}, ComponentA);
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), '', {}, ComponentB);
});
}); });
it('should set url segment and index properly', () => { it('should set url segment and index properly', () => {
const url = tree(""); const url = tree('');
recognize(RootComponent, [ recognize(
{path: '', component: ComponentA, children: [{path: '', component: ComponentB}]} RootComponent,
], url, "").forEach((s:RouterStateSnapshot) => { [{path: '', component: ComponentA, children: [{path: '', component: ComponentB}]}], url,
expect(s.root._urlSegment).toBe(url.root); '')
expect(s.root._lastPathIndex).toBe(-1); .forEach((s: RouterStateSnapshot) => {
expect(s.root._urlSegment).toBe(url.root);
expect(s.root._lastPathIndex).toBe(-1);
const c = s.firstChild(s.root); const c = s.firstChild(s.root);
expect(c._urlSegment).toBe(url.root); expect(c._urlSegment).toBe(url.root);
expect(c._lastPathIndex).toBe(-1); expect(c._lastPathIndex).toBe(-1);
const c2 = s.firstChild(<any>s.firstChild(s.root)); const c2 = s.firstChild(<any>s.firstChild(s.root));
expect(c2._urlSegment).toBe(url.root); expect(c2._urlSegment).toBe(url.root);
expect(c2._lastPathIndex).toBe(-1); expect(c2._lastPathIndex).toBe(-1);
}); });
}); });
it("should support index routes", () => { it('should support index routes', () => {
recognize(RootComponent, [ recognize(
{path: 'a', component: ComponentA, children: [ RootComponent,
{path: '', component: ComponentB} [{path: 'a', component: ComponentA, children: [{path: '', component: ComponentB}]}],
]} tree('a'), 'a')
], tree("a"), "a").forEach((s:RouterStateSnapshot) => { .forEach((s: RouterStateSnapshot) => {
checkActivatedRoute(s.firstChild(s.root), "a", {}, ComponentA); checkActivatedRoute(s.firstChild(s.root), 'a', {}, ComponentA);
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {}, ComponentB); checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), '', {}, ComponentB);
}); });
}); });
it("should support index routes with children", () => { it('should support index routes with children', () => {
recognize(RootComponent, [ recognize(
{ RootComponent, [{
path: '', component: ComponentA, children: [ path: '',
{ path: '', component: ComponentB, children: [ component: ComponentA,
{path: 'c/:id', component: ComponentC} children: [{
] path: '',
} component: ComponentB,
] children: [{path: 'c/:id', component: ComponentC}]
} }]
], tree("c/10"), "c/10").forEach((s:RouterStateSnapshot) => { }],
checkActivatedRoute(s.firstChild(s.root), "", {}, ComponentA); tree('c/10'), 'c/10')
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {}, ComponentB); .forEach((s: RouterStateSnapshot) => {
checkActivatedRoute( checkActivatedRoute(s.firstChild(s.root), '', {}, ComponentA);
s.firstChild(<any>s.firstChild(<any>s.firstChild(s.root))), "c/10", {id: '10'}, ComponentC); checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), '', {}, ComponentB);
}); checkActivatedRoute(
s.firstChild(<any>s.firstChild(<any>s.firstChild(s.root))), 'c/10', {id: '10'},
ComponentC);
});
}); });
xit("should pass parameters to every nested index route (case with non-index route)", () => { xit('should pass parameters to every nested index route (case with non-index route)', () => {
recognize(RootComponent, [ recognize(
{path: 'a', component: ComponentA, children: [{path: '', component: ComponentB}]} RootComponent,
], tree("/a;a=1"), "/a;a=1").forEach((s:RouterStateSnapshot) => { [{path: 'a', component: ComponentA, children: [{path: '', component: ComponentB}]}],
checkActivatedRoute(s.firstChild(s.root), "a", {a: '1'}, ComponentA); tree('/a;a=1'), '/a;a=1')
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {a: '1'}, ComponentB); .forEach((s: RouterStateSnapshot) => {
}); checkActivatedRoute(s.firstChild(s.root), 'a', {a: '1'}, ComponentA);
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), '', {a: '1'}, ComponentB);
});
}); });
}); });
describe("wildcards", () => { describe('wildcards', () => {
it("should support simple wildcards", () => { it('should support simple wildcards', () => {
checkRecognize([ checkRecognize(
{path: '**', component: ComponentA} [{path: '**', component: ComponentA}], 'a/b/c/d;a1=11', (s: RouterStateSnapshot) => {
], "a/b/c/d;a1=11", (s:RouterStateSnapshot) => { checkActivatedRoute(s.firstChild(s.root), 'a/b/c/d', {a1: '11'}, ComponentA);
checkActivatedRoute(s.firstChild(s.root), "a/b/c/d", {a1:'11'}, ComponentA); });
});
}); });
}); });
describe("componentless routes", () => { describe('componentless routes', () => {
it("should work", () => { it('should work', () => {
checkRecognize([ checkRecognize(
{ [{
path: 'p/:id', path: 'p/:id',
children: [ children: [
{path: 'a', component: ComponentA}, {path: 'a', component: ComponentA},
{path: 'b', component: ComponentB, outlet: 'aux'} {path: 'b', component: ComponentB, outlet: 'aux'}
] ]
} }],
], "p/11;pp=22/(a;pa=33//aux:b;pb=44)", (s:RouterStateSnapshot) => { 'p/11;pp=22/(a;pa=33//aux:b;pb=44)', (s: RouterStateSnapshot) => {
const p = s.firstChild(s.root); const p = s.firstChild(s.root);
checkActivatedRoute(p, "p/11", {id: '11', pp: '22'}, undefined); checkActivatedRoute(p, 'p/11', {id: '11', pp: '22'}, undefined);
const c = s.children(p); const c = s.children(p);
checkActivatedRoute(c[0], "a", {id: '11', pp: '22', pa: '33'}, ComponentA); checkActivatedRoute(c[0], 'a', {id: '11', pp: '22', pa: '33'}, ComponentA);
checkActivatedRoute(c[1], "b", {id: '11', pp: '22', pb: '44'}, ComponentB, "aux"); checkActivatedRoute(c[1], 'b', {id: '11', pp: '22', pb: '44'}, ComponentB, 'aux');
}); });
}); });
it("should merge params until encounters a normal route", () => { it('should merge params until encounters a normal route', () => {
checkRecognize([ checkRecognize(
{ [{
path: 'p/:id', path: 'p/:id',
children: [ children: [{
{path: 'a/:name', children: [ path: 'a/:name',
{path: 'b', component: ComponentB, children: [ children: [{
{path: 'c', component: ComponentC} path: 'b',
]} component: ComponentB,
]} children: [{path: 'c', component: ComponentC}]
] }]
} }]
], "p/11/a/victor/b/c", (s:RouterStateSnapshot) => { }],
const p = s.firstChild(s.root); 'p/11/a/victor/b/c', (s: RouterStateSnapshot) => {
checkActivatedRoute(p, "p/11", {id: '11'}, undefined); const p = s.firstChild(s.root);
checkActivatedRoute(p, 'p/11', {id: '11'}, undefined);
const a = s.firstChild(p); const a = s.firstChild(p);
checkActivatedRoute(a, "a/victor", {id: '11', name: 'victor'}, undefined); checkActivatedRoute(a, 'a/victor', {id: '11', name: 'victor'}, undefined);
const b = s.firstChild(a); const b = s.firstChild(a);
checkActivatedRoute(b, "b", {id: '11', name: 'victor'}, ComponentB); checkActivatedRoute(b, 'b', {id: '11', name: 'victor'}, ComponentB);
const c = s.firstChild(b); const c = s.firstChild(b);
checkActivatedRoute(c, "c", {}, ComponentC); checkActivatedRoute(c, 'c', {}, ComponentC);
}); });
}); });
xit("should work with empty paths", () => { xit('should work with empty paths', () => {
checkRecognize([ checkRecognize(
{ [{
path: 'p/:id', path: 'p/:id',
children: [ children: [
{path: '', component: ComponentA}, {path: '', component: ComponentA},
{path: '', component: ComponentB, outlet: 'aux'} {path: '', component: ComponentB, outlet: 'aux'}
] ]
} }],
], "p/11", (s:RouterStateSnapshot) => { 'p/11', (s: RouterStateSnapshot) => {
const p = s.firstChild(s.root); const p = s.firstChild(s.root);
checkActivatedRoute(p, "p/11", {id: '11'}, undefined); checkActivatedRoute(p, 'p/11', {id: '11'}, undefined);
const c = s.children(p); const c = s.children(p);
console.log("lsfs", c); console.log('lsfs', c);
checkActivatedRoute(c[0], "", {}, ComponentA); checkActivatedRoute(c[0], '', {}, ComponentA);
checkActivatedRoute(c[1], "", {}, ComponentB, "aux"); checkActivatedRoute(c[1], '', {}, ComponentB, 'aux');
}); });
}); });
xit("should work with empty paths and params", () => { xit('should work with empty paths and params', () => {
checkRecognize([ checkRecognize(
{ [{
path: 'p/:id', path: 'p/:id',
children: [ children: [
{path: '', component: ComponentA}, {path: '', component: ComponentA},
{path: '', component: ComponentB, outlet: 'aux'} {path: '', component: ComponentB, outlet: 'aux'}
] ]
} }],
], "p/11/(;pa=33//aux:;pb=44)", (s:RouterStateSnapshot) => { 'p/11/(;pa=33//aux:;pb=44)', (s: RouterStateSnapshot) => {
const p = s.firstChild(s.root); const p = s.firstChild(s.root);
checkActivatedRoute(p, "p/11", {id: '11'}, undefined); checkActivatedRoute(p, 'p/11', {id: '11'}, undefined);
const c = s.children(p); const c = s.children(p);
checkActivatedRoute(c[0], "", {pa: '33'}, ComponentA); checkActivatedRoute(c[0], '', {pa: '33'}, ComponentA);
checkActivatedRoute(c[1], "", {pb: '44'}, ComponentB, "aux"); checkActivatedRoute(c[1], '', {pb: '44'}, ComponentB, 'aux');
}); });
}); });
xit("should work with only aux path", () => { xit('should work with only aux path', () => {
checkRecognize([ checkRecognize(
{ [{
path: 'p/:id', path: 'p/:id',
children: [ children: [
{path: '', component: ComponentA}, {path: '', component: ComponentA},
{path: '', component: ComponentB, outlet: 'aux'} {path: '', component: ComponentB, outlet: 'aux'}
] ]
} }],
], "p/11", (s:RouterStateSnapshot) => { 'p/11', (s: RouterStateSnapshot) => {
const p = s.firstChild(s.root); const p = s.firstChild(s.root);
checkActivatedRoute(p, "p/11(aux:;pb=44)", {id: '11'}, undefined); checkActivatedRoute(p, 'p/11(aux:;pb=44)', {id: '11'}, undefined);
const c = s.children(p); const c = s.children(p);
checkActivatedRoute(c[0], "", {}, ComponentA); checkActivatedRoute(c[0], '', {}, ComponentA);
checkActivatedRoute(c[1], "", {pb: '44'}, ComponentB, "aux"); checkActivatedRoute(c[1], '', {pb: '44'}, ComponentB, 'aux');
}); });
}); });
}); });
describe("query parameters", () => { describe('query parameters', () => {
it("should support query params", () => { it('should support query params', () => {
const config = [{path: 'a', component: ComponentA}]; const config = [{path: 'a', component: ComponentA}];
checkRecognize(config, "a?q=11", (s:RouterStateSnapshot) => { checkRecognize(config, 'a?q=11', (s: RouterStateSnapshot) => {
expect(s.queryParams).toEqual({q: '11'}); expect(s.queryParams).toEqual({q: '11'});
}); });
}); });
}); });
describe("fragment", () => { describe('fragment', () => {
it("should support fragment", () => { it('should support fragment', () => {
const config = [{path: 'a', component: ComponentA}]; const config = [{path: 'a', component: ComponentA}];
checkRecognize(config, "a#f1", (s:RouterStateSnapshot) => { checkRecognize(
expect(s.fragment).toEqual("f1"); config, 'a#f1', (s: RouterStateSnapshot) => { expect(s.fragment).toEqual('f1'); });
});
}); });
}); });
describe("error handling", () => { describe('error handling', () => {
it('should error when two routes with the same outlet name got matched', () => { it('should error when two routes with the same outlet name got matched', () => {
recognize(RootComponent, [ recognize(
{ path: 'a', component: ComponentA }, RootComponent,
{ path: 'b', component: ComponentB, outlet: 'aux' }, [
{ path: 'c', component: ComponentC, outlet: 'aux' } {path: 'a', component: ComponentA}, {path: 'b', component: ComponentB, outlet: 'aux'},
], tree("a(aux:b//aux:c)"), "a(aux:b//aux:c)").subscribe((_) => {}, (s:RouterStateSnapshot) => { {path: 'c', component: ComponentC, outlet: 'aux'}
expect(s.toString()).toContain("Two segments cannot have the same outlet name: 'aux:b' and 'aux:c'."); ],
}); tree('a(aux:b//aux:c)'), 'a(aux:b//aux:c)')
.subscribe((_) => {}, (s: RouterStateSnapshot) => {
expect(s.toString())
.toContain(
'Two segments cannot have the same outlet name: \'aux:b\' and \'aux:c\'.');
});
}); });
it("should error when no matching routes", () => { it('should error when no matching routes', () => {
recognize(RootComponent, [ recognize(RootComponent, [{path: 'a', component: ComponentA}], tree('invalid'), 'invalid')
{ path: 'a', component: ComponentA } .subscribe((_) => {}, (s: RouterStateSnapshot) => {
], tree("invalid"), "invalid").subscribe((_) => {}, (s:RouterStateSnapshot) => { expect(s.toString()).toContain('Cannot match any routes');
expect(s.toString()).toContain("Cannot match any routes"); });
});
}); });
it("should error when no matching routes (too short)", () => { it('should error when no matching routes (too short)', () => {
recognize(RootComponent, [ recognize(RootComponent, [{path: 'a/:id', component: ComponentA}], tree('a'), 'a')
{ path: 'a/:id', component: ComponentA } .subscribe((_) => {}, (s: RouterStateSnapshot) => {
], tree("a"), "a").subscribe((_) => {}, (s:RouterStateSnapshot) => { expect(s.toString()).toContain('Cannot match any routes');
expect(s.toString()).toContain("Cannot match any routes"); });
});
}); });
}); });
}); });
function checkRecognize(config: RouterConfig, url: string, callback: any): void { function checkRecognize(config: RouterConfig, url: string, callback: any): void {
recognize(RootComponent, config, tree(url), url).subscribe(callback, e => { recognize(RootComponent, config, tree(url), url).subscribe(callback, e => { throw e; });
throw e;
});
} }
function checkActivatedRoute(actual: ActivatedRouteSnapshot, url: string, params: Params, cmp: Function, outlet: string = PRIMARY_OUTLET):void { function checkActivatedRoute(
actual: ActivatedRouteSnapshot, url: string, params: Params, cmp: Function,
outlet: string = PRIMARY_OUTLET): void {
if (actual === null) { if (actual === null) {
expect(actual).not.toBeNull(); expect(actual).not.toBeNull();
} else { } else {
expect(actual.url.map(s => s.path).join("/")).toEqual(url); expect(actual.url.map(s => s.path).join('/')).toEqual(url);
expect(actual.params).toEqual(params); expect(actual.params).toEqual(params);
expect(actual.component).toBe(cmp); expect(actual.component).toBe(cmp);
expect(actual.outlet).toEqual(outlet); expect(actual.outlet).toEqual(outlet);

View File

@ -1,42 +1,42 @@
import {DefaultUrlSerializer} from '../src/url_serializer'; import {RouterConfig} from '../src/config';
import {UrlTree, UrlSegment} from '../src/url_tree';
import {RouterStateSnapshot} from '../src/router_state';
import {recognize} from '../src/recognize'; import {recognize} from '../src/recognize';
import {resolve} from '../src/resolve'; import {resolve} from '../src/resolve';
import {RouterConfig} from '../src/config'; import {RouterStateSnapshot} from '../src/router_state';
import {DefaultUrlSerializer} from '../src/url_serializer';
import {UrlSegment, UrlTree} from '../src/url_tree';
describe('resolve', () => { describe('resolve', () => {
it('should resolve components', () => { it('should resolve components', () => {
checkResolve([ checkResolve(
{path: 'a', component: "ComponentA"} [{path: 'a', component: 'ComponentA'}], 'a', {ComponentA: 'ResolvedComponentA'},
], "a", {ComponentA: 'ResolvedComponentA'}, (resolved:RouterStateSnapshot) => { (resolved: RouterStateSnapshot) => {
expect(resolved.firstChild(resolved.root)._resolvedComponentFactory).toEqual('ResolvedComponentA'); expect(resolved.firstChild(resolved.root)._resolvedComponentFactory)
}); .toEqual('ResolvedComponentA');
});
}); });
it('should not resolve componentless routes', () => { it('should not resolve componentless routes', () => {
checkResolve([ checkResolve([{path: 'a', children: []}], 'a', {}, (resolved: RouterStateSnapshot) => {
{path: 'a', children: []}
], "a", {}, (resolved:RouterStateSnapshot) => {
expect(resolved.firstChild(resolved.root)._resolvedComponentFactory).toEqual(null); expect(resolved.firstChild(resolved.root)._resolvedComponentFactory).toEqual(null);
}); });
}); });
}); });
function checkResolve(config: RouterConfig, url: string, resolved: {[k:string]:string}, callback: any): void { function checkResolve(
config: RouterConfig, url: string, resolved: {[k: string]: string}, callback: any): void {
const resolver = { const resolver = {
resolveComponent: (component:string):Promise<any> => { resolveComponent: (component: string): Promise<any> => {
if (resolved[component]) { if (resolved[component]) {
return Promise.resolve(resolved[component]); return Promise.resolve(resolved[component]);
} else { } else {
return Promise.reject("unknown component"); return Promise.reject('unknown component');
} }
} }
}; };
recognize(RootComponent, config, tree(url), url).mergeMap(s => resolve(<any>resolver, s)).subscribe(callback, e => { recognize(RootComponent, config, tree(url), url)
throw e; .mergeMap(s => resolve(<any>resolver, s))
}); .subscribe(callback, e => { throw e; });
} }
function tree(url: string): UrlTree { function tree(url: string): UrlTree {

File diff suppressed because it is too large Load Diff

View File

@ -1,38 +1,38 @@
import {PRIMARY_OUTLET} from '../src/shared';
import {DefaultUrlSerializer, serializePath} from '../src/url_serializer'; import {DefaultUrlSerializer, serializePath} from '../src/url_serializer';
import {UrlSegment} from '../src/url_tree'; import {UrlSegment} from '../src/url_tree';
import {PRIMARY_OUTLET} from '../src/shared';
describe('url serializer', () => { describe('url serializer', () => {
const url = new DefaultUrlSerializer(); const url = new DefaultUrlSerializer();
it('should parse the root url', () => { it('should parse the root url', () => {
const tree = url.parse("/"); const tree = url.parse('/');
expectSegment(tree.root, ""); expectSegment(tree.root, '');
expect(url.serialize(tree)).toEqual("/"); expect(url.serialize(tree)).toEqual('/');
}); });
it('should parse non-empty urls', () => { it('should parse non-empty urls', () => {
const tree = url.parse("one/two"); const tree = url.parse('one/two');
expectSegment(tree.root.children[PRIMARY_OUTLET], "one/two"); expectSegment(tree.root.children[PRIMARY_OUTLET], 'one/two');
expect(url.serialize(tree)).toEqual("/one/two"); expect(url.serialize(tree)).toEqual('/one/two');
}); });
it("should parse multiple secondary segments", () => { it('should parse multiple secondary segments', () => {
const tree = url.parse("/one/two(left:three//right:four)"); const tree = url.parse('/one/two(left:three//right:four)');
expectSegment(tree.root.children[PRIMARY_OUTLET], "one/two"); expectSegment(tree.root.children[PRIMARY_OUTLET], 'one/two');
expectSegment(tree.root.children['left'], "three"); expectSegment(tree.root.children['left'], 'three');
expectSegment(tree.root.children['right'], "four"); expectSegment(tree.root.children['right'], 'four');
expect(url.serialize(tree)).toEqual("/one/two(left:three//right:four)"); expect(url.serialize(tree)).toEqual('/one/two(left:three//right:four)');
}); });
it("should parse segments with empty paths", () => { it('should parse segments with empty paths', () => {
const tree = url.parse("/one/two/(;a=1//right:;b=2)"); const tree = url.parse('/one/two/(;a=1//right:;b=2)');
const c = tree.root.children[PRIMARY_OUTLET]; const c = tree.root.children[PRIMARY_OUTLET];
expectSegment(tree.root.children[PRIMARY_OUTLET], "one/two", true); expectSegment(tree.root.children[PRIMARY_OUTLET], 'one/two', true);
expect(c.children[PRIMARY_OUTLET].pathsWithParams[0].path).toEqual(''); expect(c.children[PRIMARY_OUTLET].pathsWithParams[0].path).toEqual('');
expect(c.children[PRIMARY_OUTLET].pathsWithParams[0].parameters).toEqual({a: '1'}); expect(c.children[PRIMARY_OUTLET].pathsWithParams[0].parameters).toEqual({a: '1'});
@ -40,115 +40,115 @@ describe('url serializer', () => {
expect(c.children['right'].pathsWithParams[0].path).toEqual(''); expect(c.children['right'].pathsWithParams[0].path).toEqual('');
expect(c.children['right'].pathsWithParams[0].parameters).toEqual({b: '2'}); expect(c.children['right'].pathsWithParams[0].parameters).toEqual({b: '2'});
expect(url.serialize(tree)).toEqual("/one/two/(;a=1//right:;b=2)"); expect(url.serialize(tree)).toEqual('/one/two/(;a=1//right:;b=2)');
}); });
it("should parse segments with empty paths (only aux)", () => { it('should parse segments with empty paths (only aux)', () => {
const tree = url.parse("/one/two/(right:;b=2)"); const tree = url.parse('/one/two/(right:;b=2)');
const c = tree.root.children[PRIMARY_OUTLET]; const c = tree.root.children[PRIMARY_OUTLET];
expectSegment(tree.root.children[PRIMARY_OUTLET], "one/two", true); expectSegment(tree.root.children[PRIMARY_OUTLET], 'one/two', true);
expect(c.children['right'].pathsWithParams[0].path).toEqual(''); expect(c.children['right'].pathsWithParams[0].path).toEqual('');
expect(c.children['right'].pathsWithParams[0].parameters).toEqual({b: '2'}); expect(c.children['right'].pathsWithParams[0].parameters).toEqual({b: '2'});
expect(url.serialize(tree)).toEqual("/one/two/(right:;b=2)"); expect(url.serialize(tree)).toEqual('/one/two/(right:;b=2)');
}); });
it("should parse scoped secondary segments", () => { it('should parse scoped secondary segments', () => {
const tree = url.parse("/one/(two//left:three)"); const tree = url.parse('/one/(two//left:three)');
const primary = tree.root.children[PRIMARY_OUTLET]; const primary = tree.root.children[PRIMARY_OUTLET];
expectSegment(primary, "one", true); expectSegment(primary, 'one', true);
expectSegment(primary.children[PRIMARY_OUTLET], "two"); expectSegment(primary.children[PRIMARY_OUTLET], 'two');
expectSegment(primary.children["left"], "three"); expectSegment(primary.children['left'], 'three');
expect(url.serialize(tree)).toEqual("/one/(two//left:three)"); expect(url.serialize(tree)).toEqual('/one/(two//left:three)');
}); });
it("should parse scoped secondary segments with unscoped ones", () => { it('should parse scoped secondary segments with unscoped ones', () => {
const tree = url.parse("/one/(two//left:three)(right:four)"); const tree = url.parse('/one/(two//left:three)(right:four)');
const primary = tree.root.children[PRIMARY_OUTLET]; const primary = tree.root.children[PRIMARY_OUTLET];
expectSegment(primary, "one", true); expectSegment(primary, 'one', true);
expectSegment(primary.children[PRIMARY_OUTLET], "two"); expectSegment(primary.children[PRIMARY_OUTLET], 'two');
expectSegment(primary.children["left"], "three"); expectSegment(primary.children['left'], 'three');
expectSegment(tree.root.children["right"], "four"); expectSegment(tree.root.children['right'], 'four');
expect(url.serialize(tree)).toEqual("/one/(two//left:three)(right:four)"); expect(url.serialize(tree)).toEqual('/one/(two//left:three)(right:four)');
}); });
it("should parse secondary segments that have children", () => { it('should parse secondary segments that have children', () => {
const tree = url.parse("/one(left:two/three)"); const tree = url.parse('/one(left:two/three)');
expectSegment(tree.root.children[PRIMARY_OUTLET], "one"); expectSegment(tree.root.children[PRIMARY_OUTLET], 'one');
expectSegment(tree.root.children['left'], "two/three"); expectSegment(tree.root.children['left'], 'two/three');
expect(url.serialize(tree)).toEqual("/one(left:two/three)"); expect(url.serialize(tree)).toEqual('/one(left:two/three)');
}); });
it("should parse an empty secondary segment group", () => { it('should parse an empty secondary segment group', () => {
const tree = url.parse("/one()"); const tree = url.parse('/one()');
expectSegment(tree.root.children[PRIMARY_OUTLET], "one"); expectSegment(tree.root.children[PRIMARY_OUTLET], 'one');
expect(url.serialize(tree)).toEqual("/one"); expect(url.serialize(tree)).toEqual('/one');
}); });
it("should parse key-value matrix params", () => { it('should parse key-value matrix params', () => {
const tree = url.parse("/one;a=11a;b=11b(left:two;c=22//right:three;d=33)"); const tree = url.parse('/one;a=11a;b=11b(left:two;c=22//right:three;d=33)');
expectSegment(tree.root.children[PRIMARY_OUTLET], "one;a=11a;b=11b"); expectSegment(tree.root.children[PRIMARY_OUTLET], 'one;a=11a;b=11b');
expectSegment(tree.root.children["left"], "two;c=22"); expectSegment(tree.root.children['left'], 'two;c=22');
expectSegment(tree.root.children["right"], "three;d=33"); expectSegment(tree.root.children['right'], 'three;d=33');
expect(url.serialize(tree)).toEqual("/one;a=11a;b=11b(left:two;c=22//right:three;d=33)"); expect(url.serialize(tree)).toEqual('/one;a=11a;b=11b(left:two;c=22//right:three;d=33)');
}); });
it("should parse key only matrix params", () => { it('should parse key only matrix params', () => {
const tree = url.parse("/one;a"); const tree = url.parse('/one;a');
expectSegment(tree.root.children[PRIMARY_OUTLET], "one;a=true"); expectSegment(tree.root.children[PRIMARY_OUTLET], 'one;a=true');
expect(url.serialize(tree)).toEqual("/one;a=true"); expect(url.serialize(tree)).toEqual('/one;a=true');
}); });
it("should parse query params", () => { it('should parse query params', () => {
const tree = url.parse("/one?a=1&b=2"); const tree = url.parse('/one?a=1&b=2');
expect(tree.queryParams).toEqual({a: '1', b: '2'}); expect(tree.queryParams).toEqual({a: '1', b: '2'});
}); });
it("should parse query params when with parenthesis", () => { it('should parse query params when with parenthesis', () => {
const tree = url.parse("/one?a=(11)&b=(22)"); const tree = url.parse('/one?a=(11)&b=(22)');
expect(tree.queryParams).toEqual({a: '(11)', b: '(22)'}); expect(tree.queryParams).toEqual({a: '(11)', b: '(22)'});
}); });
it("should parse key only query params", () => { it('should parse key only query params', () => {
const tree = url.parse("/one?a"); const tree = url.parse('/one?a');
expect(tree.queryParams).toEqual({a: 'true'}); expect(tree.queryParams).toEqual({a: 'true'});
}); });
it("should serializer query params", () => { it('should serializer query params', () => {
const tree = url.parse("/one?a"); const tree = url.parse('/one?a');
expect(url.serialize(tree)).toEqual("/one?a=true"); expect(url.serialize(tree)).toEqual('/one?a=true');
}); });
it("should parse fragment", () => { it('should parse fragment', () => {
const tree = url.parse("/one#two"); const tree = url.parse('/one#two');
expect(tree.fragment).toEqual("two"); expect(tree.fragment).toEqual('two');
expect(url.serialize(tree)).toEqual("/one#two"); expect(url.serialize(tree)).toEqual('/one#two');
}); });
it("should parse empty fragment", () => { it('should parse empty fragment', () => {
const tree = url.parse("/one#"); const tree = url.parse('/one#');
expect(tree.fragment).toEqual(""); expect(tree.fragment).toEqual('');
expect(url.serialize(tree)).toEqual("/one#"); expect(url.serialize(tree)).toEqual('/one#');
}); });
}); });
function expectSegment(segment:UrlSegment, expected:string, hasChildren: boolean = false):void { function expectSegment(segment: UrlSegment, expected: string, hasChildren: boolean = false): void {
const p = segment.pathsWithParams.map(p => serializePath(p)).join("/"); const p = segment.pathsWithParams.map(p => serializePath(p)).join('/');
expect(p).toEqual(expected); expect(p).toEqual(expected);
expect(Object.keys(segment.children).length > 0).toEqual(hasChildren); expect(Object.keys(segment.children).length > 0).toEqual(hasChildren);
} }

View File

@ -4,63 +4,63 @@ import {UrlTree, containsTree} from '../src/url_tree';
describe('UrlTree', () => { describe('UrlTree', () => {
const serializer = new DefaultUrlSerializer(); const serializer = new DefaultUrlSerializer();
describe("containsTree", () => { describe('containsTree', () => {
describe("exact = true", () => { describe('exact = true', () => {
it("should return true when two tree are the same", () => { it('should return true when two tree are the same', () => {
const url = "/one/(one//left:three)(right:four)"; const url = '/one/(one//left:three)(right:four)';
const t1 = serializer.parse(url); const t1 = serializer.parse(url);
const t2 = serializer.parse(url); const t2 = serializer.parse(url);
expect(containsTree(t1, t2, true)).toBe(true); expect(containsTree(t1, t2, true)).toBe(true);
expect(containsTree(t2, t1, true)).toBe(true); expect(containsTree(t2, t1, true)).toBe(true);
}); });
it("should return false when paths are not the same", () => { it('should return false when paths are not the same', () => {
const t1 = serializer.parse("/one/two(right:three)"); const t1 = serializer.parse('/one/two(right:three)');
const t2 = serializer.parse("/one/two2(right:three)"); const t2 = serializer.parse('/one/two2(right:three)');
expect(containsTree(t1, t2, true)).toBe(false); expect(containsTree(t1, t2, true)).toBe(false);
}); });
it("should return false when container has an extra child", () => { it('should return false when container has an extra child', () => {
const t1 = serializer.parse("/one/two(right:three)"); const t1 = serializer.parse('/one/two(right:three)');
const t2 = serializer.parse("/one/two"); const t2 = serializer.parse('/one/two');
expect(containsTree(t1, t2, true)).toBe(false); expect(containsTree(t1, t2, true)).toBe(false);
}); });
it("should return false when containee has an extra child", () => { it('should return false when containee has an extra child', () => {
const t1 = serializer.parse("/one/two"); const t1 = serializer.parse('/one/two');
const t2 = serializer.parse("/one/two(right:three)"); const t2 = serializer.parse('/one/two(right:three)');
expect(containsTree(t1, t2, true)).toBe(false); expect(containsTree(t1, t2, true)).toBe(false);
}); });
}); });
describe("exact = false", () => { describe('exact = false', () => {
it("should return true when containee is missing a segment", () => { it('should return true when containee is missing a segment', () => {
const t1 = serializer.parse("/one/(two//left:three)(right:four)"); const t1 = serializer.parse('/one/(two//left:three)(right:four)');
const t2 = serializer.parse("/one/(two//left:three)"); const t2 = serializer.parse('/one/(two//left:three)');
expect(containsTree(t1, t2, false)).toBe(true); expect(containsTree(t1, t2, false)).toBe(true);
}); });
it("should return true when containee is missing some paths", () => { it('should return true when containee is missing some paths', () => {
const t1 = serializer.parse("/one/two/three"); const t1 = serializer.parse('/one/two/three');
const t2 = serializer.parse("/one/two"); const t2 = serializer.parse('/one/two');
expect(containsTree(t1, t2, false)).toBe(true); expect(containsTree(t1, t2, false)).toBe(true);
}); });
it("should return true container has its paths splitted into multiple segments", () => { it('should return true container has its paths splitted into multiple segments', () => {
const t1 = serializer.parse("/one/(two//left:three)"); const t1 = serializer.parse('/one/(two//left:three)');
const t2 = serializer.parse("/one/two"); const t2 = serializer.parse('/one/two');
expect(containsTree(t1, t2, false)).toBe(true); expect(containsTree(t1, t2, false)).toBe(true);
}); });
it("should return false when containee has extra segments", () => { it('should return false when containee has extra segments', () => {
const t1 = serializer.parse("/one/two"); const t1 = serializer.parse('/one/two');
const t2 = serializer.parse("/one/(two//left:three)"); const t2 = serializer.parse('/one/(two//left:three)');
expect(containsTree(t1, t2, false)).toBe(false); expect(containsTree(t1, t2, false)).toBe(false);
}); });
it("should return containee has segments that the container does not have", () => { it('should return containee has segments that the container does not have', () => {
const t1 = serializer.parse("/one/(two//left:three)"); const t1 = serializer.parse('/one/(two//left:three)');
const t2 = serializer.parse("/one/(two//right:four)"); const t2 = serializer.parse('/one/(two//right:four)');
expect(containsTree(t1, t2, false)).toBe(false); expect(containsTree(t1, t2, false)).toBe(false);
}); });
}); });

View File

@ -1,50 +1,51 @@
import {Tree, TreeNode} from '../../src/utils/tree'; import {Tree, TreeNode} from '../../src/utils/tree';
describe('tree', () => { describe('tree', () => {
it("should return the root of the tree", () => { it('should return the root of the tree', () => {
const t = new Tree<any>(new TreeNode<number>(1, [])); const t = new Tree<any>(new TreeNode<number>(1, []));
expect(t.root).toEqual(1); expect(t.root).toEqual(1);
}); });
it("should return the parent of a node", () => { it('should return the parent of a node', () => {
const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])])); const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
expect(t.parent(1)).toEqual(null); expect(t.parent(1)).toEqual(null);
expect(t.parent(2)).toEqual(1); expect(t.parent(2)).toEqual(1);
}); });
it("should return the children of a node", () => { it('should return the children of a node', () => {
const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])])); const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
expect(t.children(1)).toEqual([2]); expect(t.children(1)).toEqual([2]);
expect(t.children(2)).toEqual([]); expect(t.children(2)).toEqual([]);
}); });
it("should return the first child of a node", () => { it('should return the first child of a node', () => {
const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])])); const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
expect(t.firstChild(1)).toEqual(2); expect(t.firstChild(1)).toEqual(2);
expect(t.firstChild(2)).toEqual(null); expect(t.firstChild(2)).toEqual(null);
}); });
it("should return the siblings of a node", () => { it('should return the siblings of a node', () => {
const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, []), new TreeNode<number>(3, [])])); const t = new Tree<any>(
new TreeNode<number>(1, [new TreeNode<number>(2, []), new TreeNode<number>(3, [])]));
expect(t.siblings(2)).toEqual([3]); expect(t.siblings(2)).toEqual([3]);
expect(t.siblings(1)).toEqual([]); expect(t.siblings(1)).toEqual([]);
}); });
it("should return the path to the root", () => { it('should return the path to the root', () => {
const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])])); const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
expect(t.pathFromRoot(2)).toEqual([1, 2]); expect(t.pathFromRoot(2)).toEqual([1, 2]);
}); });
describe("contains", () => { describe('contains', () => {
it("should work", () => { it('should work', () => {
const tree = new Tree<any>( const tree = new Tree<any>(
new TreeNode<number>(1, [new TreeNode<number>(2, []), new TreeNode<number>(3, [])])); new TreeNode<number>(1, [new TreeNode<number>(2, []), new TreeNode<number>(3, [])]));
const subtree1 = new Tree<any>(new TreeNode<number>(1, [])); const subtree1 = new Tree<any>(new TreeNode<number>(1, []));
const subtree2 = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])])); const subtree2 = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
const subtree3 = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(3, [])])); const subtree3 = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(3, [])]));
const notSubtree1 = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(4, [])])); const notSubtree1 = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(4, [])]));
const notSubtree2 = new Tree<any>( const notSubtree2 = new Tree<any>(
new TreeNode<number>(1, [new TreeNode<number>(2, [new TreeNode<number>(4, [])])])); new TreeNode<number>(1, [new TreeNode<number>(2, [new TreeNode<number>(4, [])])]));
expect(tree.contains(subtree1)).toEqual(true); expect(tree.contains(subtree1)).toEqual(true);
expect(tree.contains(subtree2)).toEqual(true); expect(tree.contains(subtree2)).toEqual(true);