feat(router): implement terminal
This commit is contained in:
@ -1,30 +1,32 @@
|
||||
import {DefaultUrlSerializer} from '../src/url_serializer';
|
||||
import {TreeNode} from '../src/utils/tree';
|
||||
import {UrlTree, UrlSegment, equalUrlSegments} from '../src/url_tree';
|
||||
import {Params, PRIMARY_OUTLET} from '../src/shared';
|
||||
import {UrlTree, UrlSegment, equalPathsWithParams} from '../src/url_tree';
|
||||
import {RouterConfig} from '../src/config';
|
||||
import {applyRedirects} from '../src/apply_redirects';
|
||||
|
||||
describe('applyRedirects', () => {
|
||||
it("should return the same url tree when no redirects", () => {
|
||||
applyRedirects(tree("/a/b"), [
|
||||
checkRedirect([
|
||||
{path: 'a', component: ComponentA, children: [{path: 'b', component: ComponentB}]}
|
||||
]).forEach(t => {
|
||||
], "/a/b", t => {
|
||||
compareTrees(t, tree('/a/b'));
|
||||
});
|
||||
});
|
||||
|
||||
it("should add new segments when needed", () => {
|
||||
applyRedirects(tree("/a/b"), [
|
||||
{path: 'a/b', redirectTo: 'a/b/c'}
|
||||
]).forEach(t => {
|
||||
checkRedirect([
|
||||
{path: 'a/b', redirectTo: 'a/b/c'},
|
||||
{path: '**', component: ComponentC}
|
||||
], "/a/b", t => {
|
||||
compareTrees(t, tree('/a/b/c'));
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle positional parameters", () => {
|
||||
applyRedirects(tree("/a/1/b/2"), [
|
||||
{path: 'a/:aid/b/:bid', redirectTo: 'newa/:aid/newb/:bid'}
|
||||
]).forEach(t => {
|
||||
checkRedirect([
|
||||
{path: 'a/:aid/b/:bid', redirectTo: 'newa/:aid/newb/:bid'},
|
||||
{path: '**', component: ComponentC}
|
||||
], "/a/1/b/2", t => {
|
||||
compareTrees(t, tree('/newa/1/newb/2'));
|
||||
});
|
||||
});
|
||||
@ -38,50 +40,122 @@ describe('applyRedirects', () => {
|
||||
});
|
||||
|
||||
it("should pass matrix parameters", () => {
|
||||
applyRedirects(tree("/a;p1=1/1;p2=2"), [
|
||||
{path: 'a/:id', redirectTo: 'd/a/:id/e'}
|
||||
]).forEach(t => {
|
||||
checkRedirect([
|
||||
{path: 'a/:id', redirectTo: 'd/a/:id/e'},
|
||||
{path: '**', component: ComponentC}
|
||||
], "/a;p1=1/1;p2=2", t => {
|
||||
compareTrees(t, tree('/d/a;p1=1/1;p2=2/e'));
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle preserve secondary routes", () => {
|
||||
applyRedirects(tree("/a/1(aux:c/d)"), [
|
||||
checkRedirect([
|
||||
{path: 'a/:id', redirectTo: 'd/a/:id/e'},
|
||||
{path: 'c/d', component: ComponentA, outlet: 'aux'}
|
||||
]).forEach(t => {
|
||||
{path: 'c/d', component: ComponentA, outlet: 'aux'},
|
||||
{path: '**', component: ComponentC}
|
||||
], "/a/1(aux:c/d)", t => {
|
||||
compareTrees(t, tree('/d/a/1/e(aux:c/d)'));
|
||||
});
|
||||
});
|
||||
|
||||
it("should redirect secondary routes", () => {
|
||||
applyRedirects(tree("/a/1(aux:c/d)"), [
|
||||
checkRedirect([
|
||||
{path: 'a/:id', component: ComponentA},
|
||||
{path: 'c/d', redirectTo: 'f/c/d/e', outlet: 'aux'}
|
||||
]).forEach(t => {
|
||||
{path: 'c/d', redirectTo: 'f/c/d/e', outlet: 'aux'},
|
||||
{path: '**', component: ComponentC, outlet: 'aux'}
|
||||
], "/a/1(aux:c/d)", t => {
|
||||
compareTrees(t, tree('/a/1(aux:f/c/d/e)'));
|
||||
});
|
||||
});
|
||||
|
||||
it("should use the configuration of the route redirected to", () => {
|
||||
checkRedirect([
|
||||
{path: 'a', component: ComponentA, children: [
|
||||
{path: 'b', component: ComponentB},
|
||||
]},
|
||||
{path: 'c', redirectTo: 'a'}
|
||||
], "c/b", t => {
|
||||
compareTrees(t, tree('a/b'));
|
||||
});
|
||||
});
|
||||
|
||||
it("should redirect empty path", () => {
|
||||
checkRedirect([
|
||||
{path: 'a', component: ComponentA, children: [
|
||||
{path: 'b', component: ComponentB},
|
||||
]},
|
||||
{path: '', redirectTo: 'a'}
|
||||
], "b", t => {
|
||||
compareTrees(t, tree('a/b'));
|
||||
});
|
||||
});
|
||||
|
||||
xit("should support nested redirects", () => {
|
||||
checkRedirect([
|
||||
{path: 'a', component: ComponentA, children: [
|
||||
{path: 'b', component: ComponentB},
|
||||
{path: '', redirectTo: 'b'}
|
||||
]},
|
||||
{path: '', redirectTo: 'a'}
|
||||
], "", t => {
|
||||
compareTrees(t, tree('a/b'));
|
||||
});
|
||||
});
|
||||
|
||||
xit("should support nested redirects (when redirected to an empty path)", () => {
|
||||
checkRedirect([
|
||||
{path: '', component: ComponentA, children: [
|
||||
{path: 'b', component: ComponentB},
|
||||
{path: '', redirectTo: 'b'}
|
||||
]},
|
||||
{path: 'a', redirectTo: ''}
|
||||
], "a", t => {
|
||||
compareTrees(t, tree('b'));
|
||||
});
|
||||
});
|
||||
|
||||
it("should redirect empty path route only when terminal", () => {
|
||||
const config = [
|
||||
{path: 'a', component: ComponentA, children: [
|
||||
{path: 'b', component: ComponentB},
|
||||
]},
|
||||
{path: '', redirectTo: 'a', terminal: true}
|
||||
];
|
||||
|
||||
applyRedirects(tree("b"), config).subscribe((_) => {
|
||||
throw "Should not be reached";
|
||||
}, e => {
|
||||
expect(e.message).toEqual("Cannot match any routes: 'b'");
|
||||
});
|
||||
});
|
||||
|
||||
it("should redirect wild cards", () => {
|
||||
applyRedirects(tree("/a/1(aux:c/d)"), [
|
||||
checkRedirect([
|
||||
{path: '404', component: ComponentA},
|
||||
{path: '**', redirectTo: '/404'},
|
||||
]).forEach(t => {
|
||||
], "/a/1(aux:c/d)", t => {
|
||||
compareTrees(t, tree('/404'));
|
||||
});
|
||||
});
|
||||
|
||||
it("should support global redirects", () => {
|
||||
applyRedirects(tree("/a/b/1"), [
|
||||
checkRedirect([
|
||||
{path: 'a', component: ComponentA, children: [
|
||||
{path: 'b/:id', redirectTo: '/global/:id'}
|
||||
]},
|
||||
]).forEach(t => {
|
||||
{path: '**', component: ComponentC}
|
||||
], "/a/b/1", t => {
|
||||
compareTrees(t, tree('/global/1'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function checkRedirect(config: RouterConfig, url: string, callback: any): void {
|
||||
applyRedirects(tree(url), config).subscribe(callback, e => {
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
function tree(url: string): UrlTree {
|
||||
return new DefaultUrlSerializer().parse(url);
|
||||
}
|
||||
@ -89,19 +163,18 @@ function tree(url: string): UrlTree {
|
||||
function compareTrees(actual: UrlTree, expected: UrlTree): void{
|
||||
const serializer = new DefaultUrlSerializer();
|
||||
const error = `"${serializer.serialize(actual)}" is not equal to "${serializer.serialize(expected)}"`;
|
||||
compareNode(actual._root, expected._root, error);
|
||||
compareSegments(actual.root, expected.root, error);
|
||||
}
|
||||
|
||||
function compareNode(actual: TreeNode<UrlSegment>, expected: TreeNode<UrlSegment>, error: string): void{
|
||||
expect(equalUrlSegments([actual.value], [expected.value])).toEqual(true, error);
|
||||
function compareSegments(actual: UrlSegment, expected: UrlSegment, error: string): void{
|
||||
expect(actual).toBeDefined(error);
|
||||
expect(equalPathsWithParams(actual.pathsWithParams, expected.pathsWithParams)).toEqual(true, error);
|
||||
|
||||
expect(actual.children.length).toEqual(expected.children.length, error);
|
||||
expect(Object.keys(actual.children).length).toEqual(Object.keys(expected.children).length, error);
|
||||
|
||||
if (actual.children.length === expected.children.length) {
|
||||
for (let i = 0; i < actual.children.length; ++i) {
|
||||
compareNode(actual.children[i], expected.children[i], error);
|
||||
}
|
||||
}
|
||||
Object.keys(expected.children).forEach(key => {
|
||||
compareSegments(actual.children[key], expected.children[key], error);
|
||||
});
|
||||
}
|
||||
|
||||
class ComponentA {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {DefaultUrlSerializer} from '../src/url_serializer';
|
||||
import {UrlTree} from '../src/url_tree';
|
||||
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';
|
||||
@ -8,7 +8,7 @@ import {recognize} from '../src/recognize';
|
||||
import {RouterConfig} from '../src/config';
|
||||
|
||||
describe('create router state', () => {
|
||||
const emptyState = () => createEmptyState(RootComponent);
|
||||
const emptyState = () => createEmptyState(new UrlTree(new UrlSegment([], {}), {}, null), RootComponent);
|
||||
|
||||
it('should work create new state', () => {
|
||||
const state = createRouterState(createState([
|
||||
@ -57,7 +57,7 @@ function advanceNode(node: TreeNode<ActivatedRoute>): void {
|
||||
|
||||
function createState(config: RouterConfig, url: string): RouterStateSnapshot {
|
||||
let res;
|
||||
recognize(RootComponent, config, tree(url)).forEach(s => res = s);
|
||||
recognize(RootComponent, config, tree(url), url).forEach(s => res = s);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {DefaultUrlSerializer} from '../src/url_serializer';
|
||||
import {UrlTree, UrlSegment} from '../src/url_tree';
|
||||
import {UrlTree, UrlPathWithParams, UrlSegment} from '../src/url_tree';
|
||||
import {ActivatedRoute, ActivatedRouteSnapshot, advanceActivatedRoute} from '../src/router_state';
|
||||
import {PRIMARY_OUTLET, Params} from '../src/shared';
|
||||
import {createUrlTree} from '../src/create_url_tree';
|
||||
@ -10,185 +10,163 @@ describe('createUrlTree', () => {
|
||||
|
||||
it("should navigate to the root", () => {
|
||||
const p = serializer.parse("/");
|
||||
const t = create(p.root, p, ["/"]);
|
||||
expect(serializer.serialize(t)).toEqual("");
|
||||
const t = createRoot(p, ["/"]);
|
||||
expect(serializer.serialize(t)).toEqual("/");
|
||||
});
|
||||
|
||||
it("should support nested segments", () => {
|
||||
const p = serializer.parse("/a/b");
|
||||
const t = create(p.root, p, ["/one", 11, "two", 22]);
|
||||
const t = createRoot(p, ["/one", 11, "two", 22]);
|
||||
expect(serializer.serialize(t)).toEqual("/one/11/two/22");
|
||||
});
|
||||
|
||||
it("should preserve secondary segments", () => {
|
||||
const p = serializer.parse("/a/11/b(right:c)");
|
||||
const t = create(p.root, p, ["/a", 11, 'd']);
|
||||
const t = createRoot(p, ["/a", 11, 'd']);
|
||||
expect(serializer.serialize(t)).toEqual("/a/11/d(right:c)");
|
||||
});
|
||||
|
||||
it("should support updating secondary segments", () => {
|
||||
const p = serializer.parse("/a(right:b)");
|
||||
const t = create(p.root, p, ["right:c", 11, 'd']);
|
||||
expect(t.children(t.root)[1].outlet).toEqual("right");
|
||||
const t = createRoot(p, ["right:c", 11, 'd']);
|
||||
expect(serializer.serialize(t)).toEqual("/a(right:c/11/d)");
|
||||
});
|
||||
|
||||
it("should support updating secondary segments (nested case)", () => {
|
||||
const p = serializer.parse("/a/b(right:c)");
|
||||
const t = create(p.root, p, ["a", "right:d", 11, 'e']);
|
||||
expect(serializer.serialize(t)).toEqual("/a/b(right:d/11/e)");
|
||||
const p = serializer.parse("/a/(b//right:c)");
|
||||
const t = createRoot(p, ["a", "right:d", 11, 'e']);
|
||||
expect(serializer.serialize(t)).toEqual("/a/(b//right:d/11/e)");
|
||||
});
|
||||
|
||||
it('should update matrix parameters', () => {
|
||||
const p = serializer.parse("/a;aa=11");
|
||||
const t = create(p.root, p, ["/a", {aa: 22, bb: 33}]);
|
||||
expect(serializer.serialize(t)).toEqual("/a;aa=22;bb=33");
|
||||
const p = serializer.parse("/a;pp=11");
|
||||
const t = createRoot(p, ["/a", {pp: 22, dd: 33}]);
|
||||
expect(serializer.serialize(t)).toEqual("/a;pp=22;dd=33");
|
||||
});
|
||||
|
||||
it('should create matrix parameters', () => {
|
||||
const p = serializer.parse("/a");
|
||||
const t = create(p.root, p, ["/a", {aa: 22, bb: 33}]);
|
||||
expect(serializer.serialize(t)).toEqual("/a;aa=22;bb=33");
|
||||
const t = createRoot(p, ["/a", {pp: 22, dd: 33}]);
|
||||
expect(serializer.serialize(t)).toEqual("/a;pp=22;dd=33");
|
||||
});
|
||||
|
||||
it('should create matrix parameters together with other segments', () => {
|
||||
const p = serializer.parse("/a");
|
||||
const t = create(p.root, 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");
|
||||
});
|
||||
|
||||
describe("node reuse", () => {
|
||||
it('should reuse nodes when path is the same', () => {
|
||||
const p = serializer.parse("/a/b");
|
||||
const t = create(p.root, p, ['/a/c']);
|
||||
|
||||
expect(t.root).toBe(p.root);
|
||||
expect(t.firstChild(t.root)).toBe(p.firstChild(p.root));
|
||||
expect(t.firstChild(<any>t.firstChild(t.root))).not.toBe(p.firstChild(<any>p.firstChild(p.root)));
|
||||
});
|
||||
|
||||
it("should create new node when params are the same", () => {
|
||||
const p = serializer.parse("/a;x=1");
|
||||
const t = create(p.root, p, ['/a', {'x': 1}]);
|
||||
|
||||
expect(t.firstChild(t.root)).toBe(p.firstChild(p.root));
|
||||
});
|
||||
|
||||
it("should create new node when params are different", () => {
|
||||
const p = serializer.parse("/a;x=1");
|
||||
const t = create(p.root, p, ['/a', {'x': 2}]);
|
||||
|
||||
expect(t.firstChild(t.root)).not.toBe(p.firstChild(p.root));
|
||||
});
|
||||
});
|
||||
|
||||
describe("relative navigation", () => {
|
||||
it("should work", () => {
|
||||
const p = serializer.parse("/a(left:ap)/c(left:cp)");
|
||||
const c = p.firstChild(p.root);
|
||||
const t = create(c, p, ["c2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a(left:ap)/c2(left:cp)");
|
||||
const p = serializer.parse("/a/(c//left:cp)(left:ap)");
|
||||
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ["c2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a/(c2//left:cp)(left:ap)");
|
||||
});
|
||||
|
||||
it("should work when the first command starts with a ./", () => {
|
||||
const p = serializer.parse("/a(left:ap)/c(left:cp)");
|
||||
const c = p.firstChild(p.root);
|
||||
const t = create(c, p, ["./c2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a(left:ap)/c2(left:cp)");
|
||||
const p = serializer.parse("/a/(c//left:cp)(left:ap)");
|
||||
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ["./c2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a/(c2//left:cp)(left:ap)");
|
||||
});
|
||||
|
||||
it("should work when the first command is ./)", () => {
|
||||
const p = serializer.parse("/a(left:ap)/c(left:cp)");
|
||||
const c = p.firstChild(p.root);
|
||||
const t = create(c, p, ["./", "c2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a(left:ap)/c2(left:cp)");
|
||||
const p = serializer.parse("/a/(c//left:cp)(left:ap)");
|
||||
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ["./", "c2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a/(c2//left:cp)(left:ap)");
|
||||
});
|
||||
|
||||
it("should work when given params", () => {
|
||||
const p = serializer.parse("/a(left:ap)/c(left:cp)");
|
||||
const c = p.firstChild(p.root);
|
||||
const t = create(c, p, [{'x': 99}]);
|
||||
expect(serializer.serialize(t)).toEqual("/a(left:ap)/c;x=99(left:cp)");
|
||||
const p = serializer.parse("/a/(c//left:cp)(left:ap)");
|
||||
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)");
|
||||
});
|
||||
|
||||
it("should support going to a parent", () => {
|
||||
const p = serializer.parse("/a(left:ap)/c(left:cp)");
|
||||
const c = p.firstChild(p.root);
|
||||
const t = create(c, p, ["../a2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a2(left:ap)");
|
||||
});
|
||||
|
||||
it("should support going to a parent (nested case)", () => {
|
||||
it("should work when index > 0", () => {
|
||||
const p = serializer.parse("/a/c");
|
||||
const c = p.firstChild(<any>p.firstChild(p.root));
|
||||
const t = create(c, p, ["../c2"]);
|
||||
const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ["c2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a/c/c2");
|
||||
});
|
||||
|
||||
it("should support going to a parent (within a segment)", () => {
|
||||
const p = serializer.parse("/a/c");
|
||||
const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ["../c2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a/c2");
|
||||
});
|
||||
|
||||
it("should work when given ../", () => {
|
||||
const p = serializer.parse("/a/c");
|
||||
const c = p.firstChild(<any>p.firstChild(p.root));
|
||||
const t = create(c, p, ["../"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a");
|
||||
});
|
||||
|
||||
it("should navigate to the root", () => {
|
||||
const p = serializer.parse("/a/c");
|
||||
const c = p.firstChild(p.root);
|
||||
const t = create(c, p, ["../"]);
|
||||
expect(serializer.serialize(t)).toEqual("");
|
||||
const t = create(p.root.children[PRIMARY_OUTLET], 1, p, ["../", "c2"]);
|
||||
expect(serializer.serialize(t)).toEqual("/a/c2");
|
||||
});
|
||||
|
||||
it("should support setting matrix params", () => {
|
||||
const p = serializer.parse("/a(left:ap)/c(left:cp)");
|
||||
const c = p.firstChild(p.root);
|
||||
const t = create(c, p, ["../", {'x': 5}]);
|
||||
const p = serializer.parse("/a/(c//left:cp)(left:ap)");
|
||||
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../', {x: 5}]);
|
||||
expect(serializer.serialize(t)).toEqual("/a;x=5(left:ap)");
|
||||
});
|
||||
|
||||
xit("should support going to a parent (across segments)", () => {
|
||||
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']);
|
||||
expect(serializer.serialize(t)).toEqual("/q2(left:ap)");
|
||||
});
|
||||
|
||||
xit("should navigate to the root", () => {
|
||||
const p = serializer.parse("/a/c");
|
||||
const t = create(p.root.children[PRIMARY_OUTLET], 0, p, ['../']);
|
||||
expect(serializer.serialize(t)).toEqual("");
|
||||
});
|
||||
|
||||
it("should throw when too many ..", () => {
|
||||
const p = serializer.parse("/a(left:ap)/c(left:cp)");
|
||||
const c = p.firstChild(p.root);
|
||||
expect(() => create(c, p, ["../../"])).toThrowError("Invalid number of '../'");
|
||||
const p = serializer.parse("/a/(c//left:cp)(left:ap)");
|
||||
expect(() => create(p.root.children[PRIMARY_OUTLET], 0, p, ['../../'])).toThrowError("Invalid number of '../'");
|
||||
});
|
||||
});
|
||||
|
||||
it("should set query params", () => {
|
||||
const p = serializer.parse("/");
|
||||
const t = create(p.root, p, [], {a: 'hey'});
|
||||
const t = createRoot(p, [], {a: 'hey'});
|
||||
expect(t.queryParams).toEqual({a: 'hey'});
|
||||
});
|
||||
|
||||
it("should stringify query params", () => {
|
||||
const p = serializer.parse("/");
|
||||
const t = create(p.root, p, [], <any>{a: 1});
|
||||
const t = createRoot(p, [], <any>{a: 1});
|
||||
expect(t.queryParams).toEqual({a: '1'});
|
||||
});
|
||||
|
||||
it("should reuse old query params when given undefined", () => {
|
||||
const p = serializer.parse("/?a=1");
|
||||
const t = create(p.root, p, [], undefined);
|
||||
const t = createRoot(p, [], undefined);
|
||||
expect(t.queryParams).toEqual({a: '1'});
|
||||
});
|
||||
|
||||
it("should set fragment", () => {
|
||||
const p = serializer.parse("/");
|
||||
const t = create(p.root, p, [], {}, "fragment");
|
||||
const t = createRoot(p, [], {}, "fragment");
|
||||
expect(t.fragment).toEqual("fragment");
|
||||
});
|
||||
|
||||
it("should reused old fragment when given undefined", () => {
|
||||
const p = serializer.parse("/#fragment");
|
||||
const t = create(p.root, p, [], undefined, undefined);
|
||||
const t = createRoot(p, [], undefined, undefined);
|
||||
expect(t.fragment).toEqual("fragment");
|
||||
});
|
||||
});
|
||||
|
||||
function create(start: UrlSegment | null, tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) {
|
||||
if (!start) {
|
||||
expect(start).toBeDefined();
|
||||
function createRoot(tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) {
|
||||
const 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);
|
||||
return createUrlTree(a, tree, commands, queryParams, fragment);
|
||||
}
|
||||
|
||||
function create(segment: UrlSegment, startIndex: number, tree: UrlTree, commands: any[], queryParams?: Params, fragment?: string) {
|
||||
if (!segment) {
|
||||
expect(segment).toBeDefined();
|
||||
}
|
||||
const s = new ActivatedRouteSnapshot([], <any>{}, PRIMARY_OUTLET, "someComponent", null, <any>start);
|
||||
const s = new ActivatedRouteSnapshot([], <any>{}, PRIMARY_OUTLET, "someComponent", null, <any>segment, startIndex);
|
||||
const a = new ActivatedRoute(new BehaviorSubject(null), new BehaviorSubject(null), PRIMARY_OUTLET, "someComponent", s);
|
||||
advanceActivatedRoute(a);
|
||||
return createUrlTree(a, tree, commands, queryParams, fragment);
|
||||
|
@ -2,28 +2,27 @@ import {DefaultUrlSerializer} from '../src/url_serializer';
|
||||
import {UrlTree} from '../src/url_tree';
|
||||
import {Params, PRIMARY_OUTLET} from '../src/shared';
|
||||
import {ActivatedRouteSnapshot} from '../src/router_state';
|
||||
import {RouterConfig} from '../src/config';
|
||||
import {recognize} from '../src/recognize';
|
||||
|
||||
describe('recognize', () => {
|
||||
it('should work', (done) => {
|
||||
recognize(RootComponent, [
|
||||
it('should work', () => {
|
||||
checkRecognize([
|
||||
{
|
||||
path: 'a', component: ComponentA
|
||||
}
|
||||
], tree("a")).forEach(s => {
|
||||
], "a", s => {
|
||||
checkActivatedRoute(s.root, "", {}, RootComponent);
|
||||
checkActivatedRoute(s.firstChild(s.root), "a", {}, ComponentA);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should support secondary routes', () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{ path: 'a', component: ComponentA },
|
||||
{ path: 'b', component: ComponentB, outlet: 'left' },
|
||||
{ path: 'c', component: ComponentC, outlet: 'right' }
|
||||
], tree("a(left:b//right:c)")).forEach(s => {
|
||||
], "a(left:b//right:c)", s => {
|
||||
const c = s.children(s.root);
|
||||
checkActivatedRoute(c[0], "a", {}, ComponentA);
|
||||
checkActivatedRoute(c[1], "b", {}, ComponentB, 'left');
|
||||
@ -31,43 +30,85 @@ describe('recognize', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should match routes in the depth first order', () => {
|
||||
it('should set url segment and index properly', () => {
|
||||
const url = tree("a(left:b//right:c)");
|
||||
recognize(RootComponent, [
|
||||
{ path: 'a', component: ComponentA },
|
||||
{ path: 'b', component: ComponentB, outlet: 'left' },
|
||||
{ path: 'c', component: ComponentC, outlet: 'right' }
|
||||
], 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);
|
||||
expect(c[0]._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
|
||||
expect(c[0]._lastPathIndex).toBe(0);
|
||||
|
||||
expect(c[1]._urlSegment).toBe(url.root.children["left"]);
|
||||
expect(c[1]._lastPathIndex).toBe(0);
|
||||
|
||||
expect(c[2]._urlSegment).toBe(url.root.children["right"]);
|
||||
expect(c[2]._lastPathIndex).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set url segment and index properly (nested case)', () => {
|
||||
const url = tree("a/b/c");
|
||||
recognize(RootComponent, [
|
||||
{ path: '/a/b', component: ComponentA, children: [
|
||||
{path: 'c', component: ComponentC}
|
||||
] },
|
||||
], url, "a/b/c").subscribe(s => {
|
||||
expect(s.root._urlSegment).toBe(url.root);
|
||||
expect(s.root._lastPathIndex).toBe(-1);
|
||||
|
||||
const compA = s.firstChild(s.root);
|
||||
expect(compA._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
|
||||
expect(compA._lastPathIndex).toBe(1);
|
||||
|
||||
const compC = s.firstChild(<any>compA);
|
||||
expect(compC._urlSegment).toBe(url.root.children[PRIMARY_OUTLET]);
|
||||
expect(compC._lastPathIndex).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should match routes in the depth first order', () => {
|
||||
checkRecognize([
|
||||
{path: 'a', component: ComponentA, children: [{path: ':id', component: ComponentB}]},
|
||||
{path: 'a/:id', component: ComponentC}
|
||||
], tree("a/paramA")).forEach(s => {
|
||||
], "a/paramA", s => {
|
||||
checkActivatedRoute(s.root, "", {}, RootComponent);
|
||||
checkActivatedRoute(s.firstChild(s.root), "a", {}, ComponentA);
|
||||
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "paramA", {id: 'paramA'}, ComponentB);
|
||||
});
|
||||
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{path: 'a', component: ComponentA},
|
||||
{path: 'a/:id', component: ComponentC}
|
||||
], tree("a/paramA")).forEach(s => {
|
||||
], "a/paramA", s => {
|
||||
checkActivatedRoute(s.root, "", {}, RootComponent);
|
||||
checkActivatedRoute(s.firstChild(s.root), "a/paramA", {id: 'paramA'}, ComponentC);
|
||||
});
|
||||
});
|
||||
|
||||
it('should use outlet name when matching secondary routes', () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{ path: 'a', component: ComponentA },
|
||||
{ path: 'b', component: ComponentB, outlet: 'left' },
|
||||
{ path: 'b', component: ComponentC, outlet: 'right' }
|
||||
], tree("a(right:b)")).forEach(s => {
|
||||
], "a(right:b)", s => {
|
||||
const c = s.children(s.root);
|
||||
checkActivatedRoute(c[0], "a", {}, ComponentA);
|
||||
checkActivatedRoute(c[1], "b", {}, ComponentC, 'right');
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle nested secondary routes', () => {
|
||||
recognize(RootComponent, [
|
||||
xit('should handle nested secondary routes', () => {
|
||||
checkRecognize([
|
||||
{ path: 'a', component: ComponentA },
|
||||
{ path: 'b', component: ComponentB, outlet: 'left' },
|
||||
{ path: 'c', component: ComponentC, outlet: 'right' }
|
||||
], tree("a(left:b(right:c))")).forEach(s => {
|
||||
], "a(left:b(right:c))", s => {
|
||||
const c = s.children(s.root);
|
||||
checkActivatedRoute(c[0], "a", {}, ComponentA);
|
||||
checkActivatedRoute(c[1], "b", {}, ComponentB, 'left');
|
||||
@ -76,12 +117,12 @@ describe('recognize', () => {
|
||||
});
|
||||
|
||||
it('should handle non top-level secondary routes', () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{ path: 'a', component: ComponentA, children: [
|
||||
{ path: 'b', component: ComponentB },
|
||||
{ path: 'c', component: ComponentC, outlet: 'left' }
|
||||
] },
|
||||
], tree("a/b(left:c))")).forEach(s => {
|
||||
], "a/(b//left:c)", s => {
|
||||
const c = s.children(<any>s.firstChild(s.root));
|
||||
checkActivatedRoute(c[0], "b", {}, ComponentB, PRIMARY_OUTLET);
|
||||
checkActivatedRoute(c[1], "c", {}, ComponentC, 'left');
|
||||
@ -89,11 +130,11 @@ describe('recognize', () => {
|
||||
});
|
||||
|
||||
it('should sort routes by outlet name', () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{ path: 'a', component: ComponentA },
|
||||
{ path: 'c', component: ComponentC, outlet: 'c' },
|
||||
{ path: 'b', component: ComponentB, outlet: 'b' }
|
||||
], tree("a(c:c//b:b)")).forEach(s => {
|
||||
], "a(c:c//b:b)", s => {
|
||||
const c = s.children(s.root);
|
||||
checkActivatedRoute(c[0], "a", {}, ComponentA);
|
||||
checkActivatedRoute(c[1], "b", {}, ComponentB, 'b');
|
||||
@ -102,52 +143,52 @@ describe('recognize', () => {
|
||||
});
|
||||
|
||||
it('should support matrix parameters', () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{
|
||||
path: 'a', component: ComponentA, children: [
|
||||
{ path: 'b', component: ComponentB },
|
||||
{ path: 'c', component: ComponentC, outlet: 'left' }
|
||||
{ path: 'b', component: ComponentB }
|
||||
]
|
||||
}
|
||||
], tree("a;a1=11;a2=22/b;b1=111;b2=222(left:c;c1=1111;c2=2222)")).forEach(s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "a", {a1: '11', a2: '22'}, ComponentA);
|
||||
const c = s.children(<any>s.firstChild(s.root));
|
||||
checkActivatedRoute(c[0], "b", {b1: '111', b2: '222'}, ComponentB);
|
||||
},
|
||||
{ path: 'c', component: ComponentC, outlet: 'left' }
|
||||
], "a;a1=11;a2=22/b;b1=111;b2=222(left:c;c1=1111;c2=2222)", s => {
|
||||
const c = s.children(s.root);
|
||||
checkActivatedRoute(c[0], "a", {a1: '11', a2: '22'}, ComponentA);
|
||||
checkActivatedRoute(s.firstChild(<any>c[0]), "b", {b1: '111', b2: '222'}, ComponentB);
|
||||
checkActivatedRoute(c[1], "c", {c1: '1111', c2: '2222'}, ComponentC, 'left');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("index", () => {
|
||||
it("should support root index routes", () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{index: true, component: ComponentA}
|
||||
], tree("")).forEach(s => {
|
||||
], "", s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "", {}, ComponentA);
|
||||
});
|
||||
});
|
||||
|
||||
it("should support nested root index routes", () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{index: true, component: ComponentA, children: [{index: true, component: ComponentB}]}
|
||||
], tree("")).forEach(s => {
|
||||
], "", s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "", {}, ComponentA);
|
||||
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {}, ComponentB);
|
||||
});
|
||||
});
|
||||
|
||||
it("should support index routes", () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{path: 'a', component: ComponentA, children: [
|
||||
{index: true, component: ComponentB}
|
||||
]}
|
||||
], tree("a")).forEach(s => {
|
||||
], "a", s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "a", {}, ComponentA);
|
||||
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {}, ComponentB);
|
||||
});
|
||||
});
|
||||
|
||||
it("should support index routes with children", () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{
|
||||
index: true, component: ComponentA, children: [
|
||||
{ index: true, component: ComponentB, children: [
|
||||
@ -156,7 +197,7 @@ describe('recognize', () => {
|
||||
}
|
||||
]
|
||||
}
|
||||
], tree("c/10")).forEach(s => {
|
||||
], "c/10", s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "", {}, ComponentA);
|
||||
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {}, ComponentB);
|
||||
checkActivatedRoute(
|
||||
@ -164,21 +205,96 @@ describe('recognize', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should pass parameters to every nested index route (case with non-index route)", () => {
|
||||
recognize(RootComponent, [
|
||||
xit("should pass parameters to every nested index route (case with non-index route)", () => {
|
||||
checkRecognize([
|
||||
{path: 'a', component: ComponentA, children: [{index: true, component: ComponentB}]}
|
||||
], tree("/a;a=1")).forEach(s => {
|
||||
], "/a;a=1", s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "a", {a: '1'}, ComponentA);
|
||||
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {a: '1'}, ComponentB);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("matching empty url", () => {
|
||||
it("should support root index routes", () => {
|
||||
recognize(RootComponent, [
|
||||
{path: '', component: ComponentA}
|
||||
], tree(""), "").forEach(s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "", {}, ComponentA);
|
||||
});
|
||||
});
|
||||
|
||||
it("should support nested root index routes", () => {
|
||||
recognize(RootComponent, [
|
||||
{path: '', component: ComponentA, children: [{path: '', component: ComponentB}]}
|
||||
], tree(""), "").forEach(s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "", {}, ComponentA);
|
||||
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {}, ComponentB);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set url segment and index properly', () => {
|
||||
const url = tree("");
|
||||
recognize(RootComponent, [
|
||||
{path: '', component: ComponentA, children: [{path: '', component: ComponentB}]}
|
||||
], url, "").forEach(s => {
|
||||
expect(s.root._urlSegment).toBe(url.root);
|
||||
expect(s.root._lastPathIndex).toBe(-1);
|
||||
|
||||
const c = s.firstChild(s.root);
|
||||
expect(c._urlSegment).toBe(url.root);
|
||||
expect(c._lastPathIndex).toBe(-1);
|
||||
|
||||
const c2 = s.firstChild(<any>s.firstChild(s.root));
|
||||
expect(c2._urlSegment).toBe(url.root);
|
||||
expect(c2._lastPathIndex).toBe(-1);
|
||||
});
|
||||
});
|
||||
|
||||
it("should support index routes", () => {
|
||||
recognize(RootComponent, [
|
||||
{path: 'a', component: ComponentA, children: [
|
||||
{path: '', component: ComponentB}
|
||||
]}
|
||||
], tree("a"), "a").forEach(s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "a", {}, ComponentA);
|
||||
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {}, ComponentB);
|
||||
});
|
||||
});
|
||||
|
||||
it("should support index routes with children", () => {
|
||||
recognize(RootComponent, [
|
||||
{
|
||||
path: '', component: ComponentA, children: [
|
||||
{ path: '', component: ComponentB, children: [
|
||||
{path: 'c/:id', component: ComponentC}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
], tree("c/10"), "c/10").forEach(s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "", {}, ComponentA);
|
||||
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)", () => {
|
||||
recognize(RootComponent, [
|
||||
{path: 'a', component: ComponentA, children: [{path: '', component: ComponentB}]}
|
||||
], tree("/a;a=1"), "/a;a=1").forEach(s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "a", {a: '1'}, ComponentA);
|
||||
checkActivatedRoute(s.firstChild(<any>s.firstChild(s.root)), "", {a: '1'}, ComponentB);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("wildcards", () => {
|
||||
it("should support simple wildcards", () => {
|
||||
recognize(RootComponent, [
|
||||
checkRecognize([
|
||||
{path: '**', component: ComponentA}
|
||||
], tree("a/b/c/d;a1=11")).forEach(s => {
|
||||
], "a/b/c/d;a1=11", s => {
|
||||
checkActivatedRoute(s.firstChild(s.root), "a/b/c/d", {a1:'11'}, ComponentA);
|
||||
});
|
||||
});
|
||||
@ -187,7 +303,7 @@ describe('recognize', () => {
|
||||
describe("query parameters", () => {
|
||||
it("should support query params", () => {
|
||||
const config = [{path: 'a', component: ComponentA}];
|
||||
recognize(RootComponent, config, tree("a?q=11")).forEach(s => {
|
||||
checkRecognize(config, "a?q=11", s => {
|
||||
expect(s.queryParams).toEqual({q: '11'});
|
||||
});
|
||||
});
|
||||
@ -196,7 +312,7 @@ describe('recognize', () => {
|
||||
describe("fragment", () => {
|
||||
it("should support fragment", () => {
|
||||
const config = [{path: 'a', component: ComponentA}];
|
||||
recognize(RootComponent, config, tree("a#f1")).forEach(s => {
|
||||
checkRecognize(config, "a#f1", s => {
|
||||
expect(s.fragment).toEqual("f1");
|
||||
});
|
||||
});
|
||||
@ -208,7 +324,7 @@ describe('recognize', () => {
|
||||
{ path: 'a', component: ComponentA },
|
||||
{ path: 'b', component: ComponentB, outlet: 'aux' },
|
||||
{ path: 'c', component: ComponentC, outlet: 'aux' }
|
||||
], tree("a(aux:b//aux:c)")).subscribe((_) => {}, s => {
|
||||
], tree("a(aux:b//aux:c)"), "a(aux:b//aux:c)").subscribe((_) => {}, s => {
|
||||
expect(s.toString()).toContain("Two segments cannot have the same outlet name: 'aux:b' and 'aux:c'.");
|
||||
});
|
||||
});
|
||||
@ -216,7 +332,7 @@ describe('recognize', () => {
|
||||
it("should error when no matching routes", () => {
|
||||
recognize(RootComponent, [
|
||||
{ path: 'a', component: ComponentA }
|
||||
], tree("invalid")).subscribe((_) => {}, s => {
|
||||
], tree("invalid"), "invalid").subscribe((_) => {}, s => {
|
||||
expect(s.toString()).toContain("Cannot match any routes");
|
||||
});
|
||||
});
|
||||
@ -224,18 +340,24 @@ describe('recognize', () => {
|
||||
it("should error when no matching routes (too short)", () => {
|
||||
recognize(RootComponent, [
|
||||
{ path: 'a/:id', component: ComponentA }
|
||||
], tree("a")).subscribe((_) => {}, s => {
|
||||
], tree("a"), "a").subscribe((_) => {}, s => {
|
||||
expect(s.toString()).toContain("Cannot match any routes");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function checkRecognize(config: RouterConfig, url: string, callback: any): void {
|
||||
recognize(RootComponent, config, tree(url), url).subscribe(callback, e => {
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
function checkActivatedRoute(actual: ActivatedRouteSnapshot | null, url: string, params: Params, cmp: Function, outlet: string = PRIMARY_OUTLET):void {
|
||||
if (actual === null) {
|
||||
expect(actual).not.toBeNull();
|
||||
} else {
|
||||
expect(actual.urlSegments.map(s => s.path).join("/")).toEqual(url);
|
||||
expect(actual.url.map(s => s.path).join("/")).toEqual(url);
|
||||
expect(actual.params).toEqual(params);
|
||||
expect(actual.component).toBe(cmp);
|
||||
expect(actual.outlet).toEqual(outlet);
|
||||
|
@ -27,6 +27,7 @@ describe("Integration", () => {
|
||||
|
||||
beforeEachProviders(() => {
|
||||
let config: RouterConfig = [
|
||||
{ path: '', component: BlankCmp },
|
||||
{ path: 'simple', component: SimpleCmp }
|
||||
];
|
||||
|
||||
@ -54,19 +55,20 @@ describe("Integration", () => {
|
||||
|
||||
router.navigateByUrl('/simple');
|
||||
advance(fixture);
|
||||
|
||||
expect(location.path()).toEqual('/simple');
|
||||
})));
|
||||
|
||||
|
||||
it('should update location when navigating',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22');
|
||||
advance(fixture);
|
||||
expect(location.path()).toEqual('/team/22');
|
||||
@ -79,6 +81,9 @@ describe("Integration", () => {
|
||||
|
||||
xit('should navigate back and forward',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'simple', component: SimpleCmp },
|
||||
@ -86,7 +91,6 @@ describe("Integration", () => {
|
||||
] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
|
||||
router.navigateByUrl('/team/33/simple');
|
||||
advance(fixture);
|
||||
@ -106,14 +110,15 @@ describe("Integration", () => {
|
||||
|
||||
it('should navigate when locations changes',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'user/:name', component: UserCmp }
|
||||
] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
|
||||
router.navigateByUrl('/team/22/user/victor');
|
||||
advance(fixture);
|
||||
|
||||
@ -125,6 +130,9 @@ describe("Integration", () => {
|
||||
|
||||
it('should support secondary routes',
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'user/:name', component: UserCmp },
|
||||
@ -132,9 +140,7 @@ describe("Integration", () => {
|
||||
] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
|
||||
router.navigateByUrl('/team/22/user/victor(right:simple)');
|
||||
router.navigateByUrl('/team/22/(user/victor//right:simple)');
|
||||
advance(fixture);
|
||||
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
@ -143,6 +149,9 @@ describe("Integration", () => {
|
||||
|
||||
it('should deactivate outlets',
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'user/:name', component: UserCmp },
|
||||
@ -150,9 +159,7 @@ describe("Integration", () => {
|
||||
] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
|
||||
router.navigateByUrl('/team/22/user/victor(right:simple)');
|
||||
router.navigateByUrl('/team/22/(user/victor//right:simple)');
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22/user/victor');
|
||||
@ -163,16 +170,18 @@ describe("Integration", () => {
|
||||
|
||||
it('should deactivate nested outlets',
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'user/:name', component: UserCmp },
|
||||
{ path: 'simple', component: SimpleCmp, outlet: 'right' }
|
||||
] }
|
||||
] },
|
||||
{ path: '', component: BlankCmp}
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
|
||||
router.navigateByUrl('/team/22/user/victor(right:simple)');
|
||||
router.navigateByUrl('/team/22/(user/victor//right:simple)');
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/');
|
||||
@ -183,12 +192,13 @@ describe("Integration", () => {
|
||||
|
||||
it('should set query params and fragment',
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'query', component: QueryParamsAndFragmentCmp }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
|
||||
router.navigateByUrl('/query?name=1#fragment1');
|
||||
advance(fixture);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('query: 1 fragment: fragment1');
|
||||
@ -200,14 +210,15 @@ describe("Integration", () => {
|
||||
|
||||
it('should push params only when they change',
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb:TestComponentBuilder) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'user/:name', component: UserCmp }
|
||||
] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
|
||||
router.navigateByUrl('/team/22/user/victor');
|
||||
advance(fixture);
|
||||
const team = fixture.debugElement.children[1].componentInstance;
|
||||
@ -225,13 +236,14 @@ describe("Integration", () => {
|
||||
|
||||
it('should work when navigating to /',
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb:TestComponentBuilder) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ index: true, component: SimpleCmp },
|
||||
{ path: '/user/:name', component: UserCmp }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
|
||||
router.navigateByUrl('/user/victor');
|
||||
advance(fixture);
|
||||
|
||||
@ -245,6 +257,9 @@ describe("Integration", () => {
|
||||
|
||||
it("should cancel in-flight navigations",
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb:TestComponentBuilder) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: '/user/:name', component: UserCmp }
|
||||
]);
|
||||
@ -252,7 +267,6 @@ describe("Integration", () => {
|
||||
const recordedEvents = [];
|
||||
router.events.forEach(e => recordedEvents.push(e));
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
router.navigateByUrl('/user/init');
|
||||
advance(fixture);
|
||||
|
||||
@ -269,7 +283,7 @@ describe("Integration", () => {
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('user fedor');
|
||||
expect(user.recordedParams).toEqual([{name: 'init'}, {name: 'fedor'}]);
|
||||
|
||||
expectEvents(router, recordedEvents.slice(2), [
|
||||
expectEvents(recordedEvents, [
|
||||
[NavigationStart, '/user/init'],
|
||||
[RoutesRecognized, '/user/init'],
|
||||
[NavigationEnd, '/user/init'],
|
||||
@ -285,6 +299,9 @@ describe("Integration", () => {
|
||||
|
||||
it("should handle failed navigations gracefully",
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb:TestComponentBuilder) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: '/user/:name', component: UserCmp }
|
||||
]);
|
||||
@ -292,9 +309,6 @@ describe("Integration", () => {
|
||||
const recordedEvents = [];
|
||||
router.events.forEach(e => recordedEvents.push(e));
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
let e;
|
||||
router.navigateByUrl('/invalid').catch(_ => e = _);
|
||||
advance(fixture);
|
||||
@ -305,7 +319,7 @@ describe("Integration", () => {
|
||||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('user fedor');
|
||||
|
||||
expectEvents(router, recordedEvents.slice(2), [
|
||||
expectEvents(recordedEvents, [
|
||||
[NavigationStart, '/invalid'],
|
||||
[NavigationError, '/invalid'],
|
||||
|
||||
@ -318,6 +332,9 @@ describe("Integration", () => {
|
||||
describe("router links", () => {
|
||||
it("should support string router links",
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'link', component: StringLinkCmp },
|
||||
@ -325,9 +342,6 @@ describe("Integration", () => {
|
||||
] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22/link');
|
||||
advance(fixture);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('team 22 { link, right: }');
|
||||
@ -342,6 +356,9 @@ describe("Integration", () => {
|
||||
|
||||
it("should support absolute router links",
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'link', component: AbsoluteLinkCmp },
|
||||
@ -349,9 +366,6 @@ describe("Integration", () => {
|
||||
] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22/link');
|
||||
advance(fixture);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('team 22 { link, right: }');
|
||||
@ -366,6 +380,9 @@ describe("Integration", () => {
|
||||
|
||||
it("should support relative router links",
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'link', component: RelativeLinkCmp },
|
||||
@ -373,9 +390,6 @@ describe("Integration", () => {
|
||||
] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22/link');
|
||||
advance(fixture);
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
@ -394,11 +408,15 @@ describe("Integration", () => {
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb) => {
|
||||
let fixture = tcb.createFakeAsync(AbsoluteLinkCmp);
|
||||
advance(fixture);
|
||||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('link');
|
||||
})));
|
||||
|
||||
it("should support query params and fragments",
|
||||
fakeAsync(inject([Router, Location, TestComponentBuilder], (router, location, tcb) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, children: [
|
||||
{ path: 'link', component: LinkWithQueryParamsAndFragment },
|
||||
@ -406,9 +424,6 @@ describe("Integration", () => {
|
||||
] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22/link');
|
||||
advance(fixture);
|
||||
|
||||
@ -426,14 +441,14 @@ describe("Integration", () => {
|
||||
|
||||
describe("redirects", () => {
|
||||
it("should work", fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: '/old/team/:id', redirectTo: 'team/:id' },
|
||||
{ path: '/team/:id', component: TeamCmp }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('old/team/22');
|
||||
advance(fixture);
|
||||
|
||||
@ -450,17 +465,17 @@ describe("Integration", () => {
|
||||
|
||||
it('works',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, canActivate: ["alwaysFalse"] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22');
|
||||
advance(fixture);
|
||||
|
||||
expect(location.path()).toEqual('');
|
||||
expect(location.path()).toEqual('/');
|
||||
})));
|
||||
});
|
||||
|
||||
@ -471,13 +486,13 @@ describe("Integration", () => {
|
||||
|
||||
it('works',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, canActivate: ["alwaysTrue"] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22');
|
||||
advance(fixture);
|
||||
|
||||
@ -496,13 +511,13 @@ describe("Integration", () => {
|
||||
|
||||
it('works',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, canActivate: [AlwaysTrue] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22');
|
||||
advance(fixture);
|
||||
|
||||
@ -519,16 +534,16 @@ describe("Integration", () => {
|
||||
|
||||
it('works',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, canActivate: ['CanActivate'] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22');
|
||||
advance(fixture);
|
||||
expect(location.path()).toEqual('');
|
||||
expect(location.path()).toEqual('/');
|
||||
})));
|
||||
});
|
||||
});
|
||||
@ -544,13 +559,13 @@ describe("Integration", () => {
|
||||
|
||||
it('works',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, canDeactivate: ["CanDeactivate"] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22');
|
||||
advance(fixture);
|
||||
|
||||
@ -579,13 +594,13 @@ describe("Integration", () => {
|
||||
|
||||
it('works',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, canDeactivate: [AlwaysTrue] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22');
|
||||
advance(fixture);
|
||||
expect(location.path()).toEqual('/team/22');
|
||||
@ -606,13 +621,13 @@ describe("Integration", () => {
|
||||
|
||||
it('works',
|
||||
fakeAsync(inject([Router, TestComponentBuilder, Location], (router, tcb, location) => {
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.resetConfig([
|
||||
{ path: 'team/:id', component: TeamCmp, canDeactivate: ['CanDeactivate'] }
|
||||
]);
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.navigateByUrl('/team/22');
|
||||
advance(fixture);
|
||||
expect(location.path()).toEqual('/team/22');
|
||||
@ -625,10 +640,10 @@ describe("Integration", () => {
|
||||
});
|
||||
});
|
||||
|
||||
function expectEvents(router: Router, events:Event[], pairs: any[]) {
|
||||
function expectEvents(events:Event[], pairs: any[]) {
|
||||
for (let i = 0; i < events.length; ++i) {
|
||||
expect((<any>events[i].constructor).name).toBe(pairs[i][0].name);
|
||||
expect(router.serializeUrl((<any>events[i]).url)).toBe(pairs[i][1]);
|
||||
expect((<any>events[i]).url).toBe(pairs[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -641,7 +656,7 @@ class StringLinkCmp {}
|
||||
|
||||
@Component({
|
||||
selector: 'link-cmp',
|
||||
template: `<a [routerLink]="['/team/33/simple']">link</a>`,
|
||||
template: `<router-outlet></router-outlet><a [routerLink]="['/team/33/simple']">link</a>`,
|
||||
directives: ROUTER_DIRECTIVES
|
||||
})
|
||||
class AbsoluteLinkCmp {}
|
||||
@ -668,6 +683,14 @@ class LinkWithQueryParamsAndFragment {}
|
||||
class SimpleCmp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'blank-cmp',
|
||||
template: ``,
|
||||
directives: ROUTER_DIRECTIVES
|
||||
})
|
||||
class BlankCmp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'team-cmp',
|
||||
template: `team {{id | async}} { <router-outlet></router-outlet>, right: <router-outlet name="right"></router-outlet> }`,
|
||||
|
@ -1,76 +1,80 @@
|
||||
import {DefaultUrlSerializer, serializeSegment} from '../src/url_serializer';
|
||||
import {DefaultUrlSerializer, serializePath} from '../src/url_serializer';
|
||||
import {UrlSegment} from '../src/url_tree';
|
||||
import {PRIMARY_OUTLET} from '../src/shared';
|
||||
|
||||
describe('url serializer', () => {
|
||||
const url = new DefaultUrlSerializer();
|
||||
|
||||
it('should parse the root url', () => {
|
||||
const tree = url.parse("/");
|
||||
|
||||
expectSegment(tree.root, "");
|
||||
expect(url.serialize(tree)).toEqual("");
|
||||
expect(url.serialize(tree)).toEqual("/");
|
||||
});
|
||||
|
||||
it('should parse non-empty urls', () => {
|
||||
const tree = url.parse("one/two");
|
||||
const one = tree.firstChild(tree.root);
|
||||
|
||||
expectSegment(one, "one");
|
||||
expectSegment(tree.firstChild(<any>one), "two");
|
||||
expectSegment(tree.root.children[PRIMARY_OUTLET], "one/two");
|
||||
expect(url.serialize(tree)).toEqual("/one/two");
|
||||
});
|
||||
|
||||
it("should parse multiple secondary segments", () => {
|
||||
const tree = url.parse("/one/two(left:three//right:four)/five");
|
||||
const c = tree.children(<any>tree.firstChild(tree.root));
|
||||
const tree = url.parse("/one/two(left:three//right:four)");
|
||||
|
||||
expectSegment(c[0], "two");
|
||||
expectSegment(c[1], "left:three");
|
||||
expectSegment(c[2], "right:four");
|
||||
expectSegment(tree.root.children[PRIMARY_OUTLET], "one/two");
|
||||
expectSegment(tree.root.children['left'], "three");
|
||||
expectSegment(tree.root.children['right'], "four");
|
||||
|
||||
expectSegment(tree.firstChild(c[0]), "five");
|
||||
|
||||
expect(url.serialize(tree)).toEqual("/one/two(left:three//right:four)/five");
|
||||
expect(url.serialize(tree)).toEqual("/one/two(left:three//right:four)");
|
||||
});
|
||||
|
||||
it("should parse secondary segments that have secondary segments", () => {
|
||||
const tree = url.parse("/one(left:two(right:three))");
|
||||
const c = tree.children(tree.root);
|
||||
it("should parse scoped secondary segments", () => {
|
||||
const tree = url.parse("/one/(two//left:three)");
|
||||
|
||||
expectSegment(c[0], "one");
|
||||
expectSegment(c[1], "left:two");
|
||||
expectSegment(c[2], "right:three");
|
||||
const primary = tree.root.children[PRIMARY_OUTLET];
|
||||
expectSegment(primary, "one", true);
|
||||
|
||||
expect(url.serialize(tree)).toEqual("/one(left:two//right:three)");
|
||||
expectSegment(primary.children[PRIMARY_OUTLET], "two");
|
||||
expectSegment(primary.children["left"], "three");
|
||||
|
||||
expect(url.serialize(tree)).toEqual("/one/(two//left:three)");
|
||||
});
|
||||
|
||||
it("should parse scoped secondary segments with unscoped ones", () => {
|
||||
const tree = url.parse("/one/(two//left:three)(right:four)");
|
||||
|
||||
const primary = tree.root.children[PRIMARY_OUTLET];
|
||||
expectSegment(primary, "one", true);
|
||||
expectSegment(primary.children[PRIMARY_OUTLET], "two");
|
||||
expectSegment(primary.children["left"], "three");
|
||||
expectSegment(tree.root.children["right"], "four");
|
||||
|
||||
expect(url.serialize(tree)).toEqual("/one/(two//left:three)(right:four)");
|
||||
});
|
||||
|
||||
it("should parse secondary segments that have children", () => {
|
||||
const tree = url.parse("/one(left:two/three)");
|
||||
const c = tree.children(tree.root);
|
||||
|
||||
expectSegment(c[0], "one");
|
||||
expectSegment(c[1], "left:two");
|
||||
expectSegment(tree.firstChild(c[1]), "three");
|
||||
expectSegment(tree.root.children[PRIMARY_OUTLET], "one");
|
||||
expectSegment(tree.root.children['left'], "two/three");
|
||||
|
||||
expect(url.serialize(tree)).toEqual("/one(left:two/three)");
|
||||
});
|
||||
|
||||
it("should parse an empty secondary segment group", () => {
|
||||
const tree = url.parse("/one()");
|
||||
const c = tree.children(tree.root);
|
||||
|
||||
expectSegment(c[0], "one");
|
||||
expect(tree.children(c[0]).length).toEqual(0);
|
||||
expectSegment(tree.root.children[PRIMARY_OUTLET], "one");
|
||||
|
||||
expect(url.serialize(tree)).toEqual("/one");
|
||||
});
|
||||
|
||||
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 c = tree.children(tree.root);
|
||||
|
||||
expectSegment(c[0], "one;a=11a;b=11b");
|
||||
expectSegment(c[1], "left:two;c=22");
|
||||
expectSegment(c[2], "right:three;d=33");
|
||||
expectSegment(tree.root.children[PRIMARY_OUTLET], "one;a=11a;b=11b");
|
||||
expectSegment(tree.root.children["left"], "two;c=22");
|
||||
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)");
|
||||
});
|
||||
@ -78,8 +82,7 @@ describe('url serializer', () => {
|
||||
it("should parse key only matrix params", () => {
|
||||
const tree = url.parse("/one;a");
|
||||
|
||||
const c = tree.firstChild(tree.root);
|
||||
expectSegment(c, "one;a=true");
|
||||
expectSegment(tree.root.children[PRIMARY_OUTLET], "one;a=true");
|
||||
|
||||
expect(url.serialize(tree)).toEqual("/one;a=true");
|
||||
});
|
||||
@ -112,6 +115,8 @@ describe('url serializer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function expectSegment(segment:UrlSegment | null, expected:string):void {
|
||||
expect(segment ? serializeSegment(segment) : null).toEqual(expected);
|
||||
function expectSegment(segment:UrlSegment, expected:string, hasChildren: boolean = false):void {
|
||||
const p = segment.pathsWithParams.map(p => serializePath(p)).join("/");
|
||||
expect(p).toEqual(expected);
|
||||
expect(Object.keys(segment.children).length > 0).toEqual(hasChildren);
|
||||
}
|
||||
|
Reference in New Issue
Block a user