refactor(router): Remove legacy url creation code (#49360)

This commit removes code for the old way of creating a `UrlTree`.

PR Close #49360
This commit is contained in:
Andrew Scott
2023-02-17 12:59:33 -08:00
committed by Pawel Kozlowski
parent 60d71484fd
commit ff9d3b0f02
11 changed files with 43 additions and 411 deletions
@@ -137,12 +137,6 @@
{
"name": "Console"
},
{
"name": "CreateUrlTreeStrategy"
},
{
"name": "CreateUrlTreeUsingSnapshot"
},
{
"name": "DEFAULT_APP_ID"
},
@@ -884,6 +878,9 @@
{
"name": "absoluteRedirect"
},
{
"name": "addEmptyPathsToChildrenIfNeeded"
},
{
"name": "addPropertyAlias"
},
@@ -1001,6 +998,9 @@
{
"name": "convertToParamMap"
},
{
"name": "createChildrenForEmptyPaths"
},
{
"name": "createContainerRef"
},
@@ -1358,9 +1358,6 @@
{
"name": "getParentInjectorView"
},
{
"name": "getPathIndexShift"
},
{
"name": "getPipeDef"
},
@@ -1382,9 +1379,6 @@
{
"name": "getSimpleChangesStore"
},
{
"name": "getSourceSegmentGroup"
},
{
"name": "getSymbolIterator"
},
+1 -53
View File
@@ -9,7 +9,7 @@
import {ɵRuntimeError as RuntimeError} from '@angular/core';
import {RuntimeErrorCode} from './errors';
import {ActivatedRoute, ActivatedRouteSnapshot} from './router_state';
import {ActivatedRouteSnapshot} from './router_state';
import {Params, PRIMARY_OUTLET} from './shared';
import {createRoot, squashSegmentGroup, UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree';
import {last, shallowEqual} from './utils/collection';
@@ -123,38 +123,6 @@ export function createUrlTreeFromSegmentGroup(
return tree(root, position.segmentGroup, newSegmentGroup, queryParams, fragment);
}
export function createUrlTree(
route: ActivatedRoute, urlTree: UrlTree, commands: any[], queryParams: Params|null,
fragment: string|null): UrlTree {
if (commands.length === 0) {
return tree(urlTree.root, urlTree.root, urlTree.root, queryParams, fragment);
}
const nav = computeNavigation(commands);
if (nav.toRoot()) {
return tree(urlTree.root, urlTree.root, new UrlSegmentGroup([], {}), queryParams, fragment);
}
function createTreeUsingPathIndex(lastPathIndex: number) {
const startingPosition =
findStartingPosition(nav, urlTree, route.snapshot?._urlSegment, lastPathIndex);
const segmentGroup = startingPosition.processChildren ?
updateSegmentGroupChildren(
startingPosition.segmentGroup, startingPosition.index, nav.commands) :
updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment);
}
// Note: The types should disallow `snapshot` from being `undefined` but due to test mocks, this
// may be the case. Since we try to access it at an earlier point before the refactor to add the
// warning for `relativeLinkResolution: 'legacy'`, this may cause failures in tests where it
// didn't before.
const result = createTreeUsingPathIndex(route.snapshot?._lastPathIndex);
return result;
}
function isMatrixParams(command: any): boolean {
return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
}
@@ -309,26 +277,6 @@ function findStartingPositionForTargetGroup(
return createPositionApplyingDoubleDots(target, index, nav.numberOfDoubleDots);
}
function findStartingPosition(
nav: Navigation, tree: UrlTree, segmentGroup: UrlSegmentGroup,
lastPathIndex: number): Position {
if (nav.isAbsolute) {
return new Position(tree.root, true, 0);
}
if (lastPathIndex === -1) {
// Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
// see issue #26224, #13011, #35687
// However, if the ActivatedRoute is the root we should process children like above.
const processChildren = segmentGroup === tree.root;
return new Position(segmentGroup, processChildren, 0);
}
const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
const index = lastPathIndex + modifier;
return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
}
function createPositionApplyingDoubleDots(
group: UrlSegmentGroup, index: number, numberOfDoubleDots: number): Position {
let g = group;
@@ -1,78 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
import {createSegmentGroupFromRoute, createUrlTree, createUrlTreeFromSegmentGroup} from './create_url_tree';
import {ActivatedRoute, RouterState} from './router_state';
import {Params} from './shared';
import {UrlSegmentGroup, UrlTree} from './url_tree';
const NG_DEV_MODE = typeof ngDevMode === 'undefined' || ngDevMode;
@Injectable()
export class LegacyCreateUrlTree implements CreateUrlTreeStrategy {
createUrlTree(
relativeTo: ActivatedRoute|null|undefined, currentState: RouterState, currentUrlTree: UrlTree,
commands: any[], queryParams: Params|null, fragment: string|null): UrlTree {
const a = relativeTo || currentState.root;
const tree = createUrlTree(a, currentUrlTree, commands, queryParams, fragment);
if (NG_DEV_MODE) {
const treeFromSnapshotStrategy = new CreateUrlTreeUsingSnapshot().createUrlTree(
relativeTo, currentState, currentUrlTree, commands, queryParams, fragment);
if (treeFromSnapshotStrategy.toString() !== tree.toString()) {
let warningString = `The navigation to ${tree.toString()} will instead go to ${
treeFromSnapshotStrategy.toString()} in an upcoming version of Angular.`;
if (!!relativeTo) {
warningString += ' `relativeTo` might need to be removed from the `UrlCreationOptions`.';
}
tree._warnIfUsedForNavigation = warningString;
}
}
return tree;
}
}
@Injectable()
export class CreateUrlTreeUsingSnapshot implements CreateUrlTreeStrategy {
createUrlTree(
relativeTo: ActivatedRoute|null|undefined, currentState: RouterState, currentUrlTree: UrlTree,
commands: any[], queryParams: Params|null, fragment: string|null): UrlTree {
let relativeToUrlSegmentGroup: UrlSegmentGroup|undefined;
try {
const relativeToSnapshot = relativeTo ? relativeTo.snapshot : currentState.snapshot.root;
relativeToUrlSegmentGroup = createSegmentGroupFromRoute(relativeToSnapshot);
} catch (e: unknown) {
// This is strictly for backwards compatibility with tests that create
// invalid `ActivatedRoute` mocks.
// Note: the difference between having this fallback for invalid `ActivatedRoute` setups and
// just throwing is ~500 test failures. Fixing all of those tests by hand is not feasible at
// the moment.
if (typeof commands[0] !== 'string' || !commands[0].startsWith('/')) {
// Navigations that were absolute in the old way of creating UrlTrees
// would still work because they wouldn't attempt to match the
// segments in the `ActivatedRoute` to the `currentUrlTree` but
// instead just replace the root segment with the navigation result.
// Non-absolute navigations would fail to apply the commands because
// the logic could not find the segment to replace (so they'd act like there were no
// commands).
commands = [];
}
relativeToUrlSegmentGroup = currentUrlTree.root;
}
return createUrlTreeFromSegmentGroup(
relativeToUrlSegmentGroup, commands, queryParams, fragment);
}
}
@Injectable({providedIn: 'root', useClass: CreateUrlTreeUsingSnapshot})
export abstract class CreateUrlTreeStrategy {
abstract createUrlTree(
relativeTo: ActivatedRoute|null|undefined, currentState: RouterState, currentUrlTree: UrlTree,
commands: any[], queryParams: Params|null, fragment: string|null): UrlTree;
}
+3 -35
View File
@@ -70,8 +70,7 @@ export class Recognizer {
// navigation, resulting in the router being out of sync with the browser.
const root = new ActivatedRouteSnapshot(
[], Object.freeze({}), Object.freeze({...this.urlTree.queryParams}),
this.urlTree.fragment, {}, PRIMARY_OUTLET, this.rootComponentType, null,
this.urlTree.root, -1, {});
this.urlTree.fragment, {}, PRIMARY_OUTLET, this.rootComponentType, null, {});
const rootNode = new TreeNode<ActivatedRouteSnapshot>(root, children);
const routeState = new RouterStateSnapshot(this.url, rootNode);
@@ -177,11 +176,10 @@ export class Recognizer {
if (route.path === '**') {
const params = segments.length > 0 ? last(segments)!.parameters : {};
const pathIndexShift = getPathIndexShift(rawSegment) + segments.length;
const snapshot = new ActivatedRouteSnapshot(
segments, params, Object.freeze({...this.urlTree.queryParams}), this.urlTree.fragment,
getData(route), getOutlet(route), route.component ?? route._loadedComponent ?? null,
route, getSourceSegmentGroup(rawSegment), pathIndexShift, getResolve(route));
route, getResolve(route));
matchResult = of({
snapshot,
consumedSegments: [],
@@ -194,13 +192,11 @@ export class Recognizer {
if (!matched) {
return null;
}
const pathIndexShift = getPathIndexShift(rawSegment) + consumedSegments.length;
const snapshot = new ActivatedRouteSnapshot(
consumedSegments, parameters, Object.freeze({...this.urlTree.queryParams}),
this.urlTree.fragment, getData(route), getOutlet(route),
route.component ?? route._loadedComponent ?? null, route,
getSourceSegmentGroup(rawSegment), pathIndexShift, getResolve(route));
route.component ?? route._loadedComponent ?? null, route, getResolve(route));
return {snapshot, consumedSegments, remainingSegments};
}));
}
@@ -335,34 +331,6 @@ function checkOutletNameUniqueness(nodes: TreeNode<ActivatedRouteSnapshot>[]): v
});
}
function getSourceSegmentGroup(segmentGroup: UrlSegmentGroup): UrlSegmentGroup {
let s = segmentGroup;
while (s._sourceSegment) {
s = s._sourceSegment;
}
return s;
}
function getPathIndexShift(segmentGroup: UrlSegmentGroup): number {
let s = segmentGroup;
let res = s._segmentIndexShift ?? 0;
while (s._sourceSegment) {
s = s._sourceSegment;
res += s._segmentIndexShift ?? 0;
}
return res - 1;
}
function getCorrectedPathIndexShift(segmentGroup: UrlSegmentGroup): number {
let s = segmentGroup;
let res = s._segmentIndexShiftCorrected ?? s._segmentIndexShift ?? 0;
while (s._sourceSegment) {
s = s._sourceSegment;
res += s._segmentIndexShiftCorrected ?? s._segmentIndexShift ?? 0;
}
return res - 1;
}
function getData(route: Route): Data {
return route.data || {};
}
+26 -7
View File
@@ -10,7 +10,7 @@ import {Location} from '@angular/common';
import {inject, Injectable, NgZone, Type, ɵConsole as Console, ɵRuntimeError as RuntimeError} from '@angular/core';
import {Observable, of, SubscriptionLike} from 'rxjs';
import {CreateUrlTreeStrategy} from './create_url_tree_strategy';
import {createSegmentGroupFromRoute, createUrlTreeFromSegmentGroup} from './create_url_tree';
import {RuntimeErrorCode} from './errors';
import {Event, IMPERATIVE_NAVIGATION, NavigationTrigger} from './events';
import {NavigationBehaviorOptions, OnSameUrlNavigation, Routes} from './models';
@@ -22,7 +22,7 @@ import {ROUTES} from './router_config_loader';
import {createEmptyState, RouterState} from './router_state';
import {Params} from './shared';
import {UrlHandlingStrategy} from './url_handling_strategy';
import {containsTree, IsActiveMatchOptions, isUrlTree, UrlSerializer, UrlTree} from './url_tree';
import {containsTree, IsActiveMatchOptions, isUrlTree, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
import {standardizeConfig, validateConfig} from './utils/config';
@@ -220,9 +220,6 @@ export class Router {
*/
routeReuseStrategy = inject(RouteReuseStrategy);
/** Strategy used to create a UrlTree. */
private readonly urlCreationStrategy = inject(CreateUrlTreeStrategy);
/**
* A strategy for setting the title based on the `routerState`.
*
@@ -531,8 +528,30 @@ export class Router {
if (q !== null) {
q = this.removeEmptyProps(q);
}
return this.urlCreationStrategy.createUrlTree(
relativeTo, this.routerState, this.currentUrlTree, commands, q, f ?? null);
let relativeToUrlSegmentGroup: UrlSegmentGroup|undefined;
try {
const relativeToSnapshot = relativeTo ? relativeTo.snapshot : this.routerState.snapshot.root;
relativeToUrlSegmentGroup = createSegmentGroupFromRoute(relativeToSnapshot);
} catch (e: unknown) {
// This is strictly for backwards compatibility with tests that create
// invalid `ActivatedRoute` mocks.
// Note: the difference between having this fallback for invalid `ActivatedRoute` setups and
// just throwing is ~500 test failures. Fixing all of those tests by hand is not feasible at
// the moment.
if (typeof commands[0] !== 'string' || !commands[0].startsWith('/')) {
// Navigations that were absolute in the old way of creating UrlTrees
// would still work because they wouldn't attempt to match the
// segments in the `ActivatedRoute` to the `currentUrlTree` but
// instead just replace the root segment with the navigation result.
// Non-absolute navigations would fail to apply the commands because
// the logic could not find the segment to replace (so they'd act like there were no
// commands).
commands = [];
}
relativeToUrlSegmentGroup = this.currentUrlTree.root;
}
return createUrlTreeFromSegmentGroup(relativeToUrlSegmentGroup, commands, q, f ?? null);
}
/**
+2 -9
View File
@@ -84,7 +84,7 @@ export function createEmptyStateSnapshot(
const fragment = '';
const activated = new ActivatedRouteSnapshot(
[], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null,
urlTree.root, -1, {});
{});
return new RouterStateSnapshot('', new TreeNode<ActivatedRouteSnapshot>(activated, []));
}
@@ -281,10 +281,6 @@ function flattenInherited(pathFromRoot: ActivatedRouteSnapshot[]): Inherited {
export class ActivatedRouteSnapshot {
/** The configuration used to match this route **/
public readonly routeConfig: Route|null;
/** @internal **/
_urlSegment: UrlSegmentGroup;
/** @internal */
_lastPathIndex: number;
/** @internal */
_resolve: ResolveData;
/** @internal */
@@ -336,11 +332,8 @@ export class ActivatedRouteSnapshot {
/** The outlet name of the route */
public outlet: string,
/** The component of the route */
public component: Type<any>|null, routeConfig: Route|null, urlSegment: UrlSegmentGroup,
lastPathIndex: number, resolve: ResolveData) {
public component: Type<any>|null, routeConfig: Route|null, resolve: ResolveData) {
this.routeConfig = routeConfig;
this._urlSegment = urlSegment;
this._lastPathIndex = lastPathIndex;
this._resolve = resolve;
}
-11
View File
@@ -233,17 +233,6 @@ export class UrlTree {
* @publicApi
*/
export class UrlSegmentGroup {
/** @internal */
_sourceSegment?: UrlSegmentGroup;
/** @internal */
_segmentIndexShift?: number;
/**
* @internal
*
* Used only in dev mode to detect if application relies on `relativeLinkResolution: 'legacy'`
* Should be removed in when `relativeLinkResolution` is removed.
*/
_segmentIndexShiftCorrected?: number;
/** The parent node in the url tree */
parent: UrlSegmentGroup|null = null;
+2 -16
View File
@@ -96,10 +96,7 @@ export function split(
const s = new UrlSegmentGroup(
consumedSegments,
createChildrenForEmptyPaths(
segmentGroup, consumedSegments, config,
new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
return {segmentGroup: s, slicedSegments: []};
}
@@ -109,14 +106,10 @@ export function split(
segmentGroup.segments,
addEmptyPathsToChildrenIfNeeded(
segmentGroup, consumedSegments, slicedSegments, config, segmentGroup.children));
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
return {segmentGroup: s, slicedSegments};
}
const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children);
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
return {segmentGroup: s, slicedSegments};
}
@@ -128,8 +121,6 @@ function addEmptyPathsToChildrenIfNeeded(
for (const r of routes) {
if (emptyPathMatch(segmentGroup, slicedSegments, r) && !children[getOutlet(r)]) {
const s = new UrlSegmentGroup([], {});
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
res[getOutlet(r)] = s;
}
}
@@ -137,18 +128,13 @@ function addEmptyPathsToChildrenIfNeeded(
}
function createChildrenForEmptyPaths(
segmentGroup: UrlSegmentGroup, consumedSegments: UrlSegment[], routes: Route[],
primarySegment: UrlSegmentGroup): {[name: string]: UrlSegmentGroup} {
routes: Route[], primarySegment: UrlSegmentGroup): {[name: string]: UrlSegmentGroup} {
const res: {[name: string]: UrlSegmentGroup} = {};
res[PRIMARY_OUTLET] = primarySegment;
primarySegment._sourceSegment = segmentGroup;
primarySegment._segmentIndexShift = consumedSegments.length;
for (const r of routes) {
if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) {
const s = new UrlSegmentGroup([], {});
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
res[getOutlet(r)] = s;
}
}
+2 -4
View File
@@ -11,7 +11,7 @@ import {Type} from '@angular/core';
import {Data, ResolveData, Route} from '../src/models';
import {ActivatedRouteSnapshot} from '../src/router_state';
import {Params} from '../src/shared';
import {UrlSegment, UrlSegmentGroup, UrlTree} from '../src/url_tree';
import {UrlSegment, UrlTree} from '../src/url_tree';
export class Logger {
logs: string[] = [];
@@ -39,8 +39,6 @@ export declare type ARSArgs = {
data?: Data,
outlet?: string, component: Type<unknown>| string | null,
routeConfig?: Route | null,
urlSegment?: UrlSegmentGroup,
lastPathIndex?: number,
resolve?: ResolveData
};
@@ -48,5 +46,5 @@ export function createActivatedRouteSnapshot(args: ARSArgs): ActivatedRouteSnaps
return new (ActivatedRouteSnapshot as any)(
args.url || [], args.params || {}, args.queryParams || null, args.fragment || null,
args.data || null, args.outlet || null, args.component, args.routeConfig || {},
args.urlSegment || null, args.lastPathIndex || -1, args.resolve || {});
args.resolve || {});
}
-185
View File
@@ -52,59 +52,13 @@ describe('recognize', async () => {
],
'a(left:b//right:c)');
expect(s.root.url.toString()).toEqual(url.root.toString());
expect((s.root as any)._lastPathIndex).toBe(-1);
const c = s.root.children;
expect(c[0].url.toString()).toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((c[0] as any)._lastPathIndex).toBe(0);
expect(c[1].url.toString()).toEqual(url.root.children['left'].toString());
expect((c[1] as any)._lastPathIndex).toBe(0);
expect(c[2].url.toString()).toEqual(url.root.children['right'].toString());
expect((c[2] as any)._lastPathIndex).toBe(0);
});
it('should set url segment and index properly (nested case)', async () => {
const url = tree('a/b/c');
const s = await recognize(
[
{path: 'a/b', component: ComponentA, children: [{path: 'c', component: ComponentC}]},
],
'a/b/c');
expect((s.root as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((s.root as any)._lastPathIndex).toBe(-1);
const compA = s.root.firstChild!;
expect((compA as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((compA as any)._lastPathIndex).toBe(1);
const compC = compA.firstChild!;
expect((compC as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((compC as any)._lastPathIndex).toBe(2);
});
it('should set url segment and index properly (wildcard)', async () => {
const url = tree('a/b/c');
const s = await recognize(
[
{path: 'a', component: ComponentA, children: [{path: '**', component: ComponentB}]},
],
'a/b/c');
expect((s.root as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((s.root as any)._lastPathIndex).toBe(-1);
const compA = s.root.firstChild!;
expect((compA as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((compA as any)._lastPathIndex).toBe(0);
const compC = compA.firstChild!;
expect((compC as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((compC as any)._lastPathIndex).toBe(2);
});
it('should match routes in the depth first order', async () => {
@@ -251,22 +205,6 @@ describe('recognize', async () => {
checkActivatedRoute(s.root.firstChild!.firstChild!, '', {}, ComponentB);
});
it('should set url segment and index properly', async () => {
const url = tree('') as any;
const s = await recognize(
[{path: '', component: ComponentA, children: [{path: '', component: ComponentB}]}], '');
expect((s.root as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((s.root as any)._lastPathIndex).toBe(-1);
const c = s.root.firstChild!;
expect((c as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((c as any)._lastPathIndex).toBe(-1);
const c2 = s.root.firstChild!.firstChild!;
expect((c2 as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((c2 as any)._lastPathIndex).toBe(-1);
});
it('should inherit params', async () => {
const s = await recognize(
[{
@@ -349,129 +287,6 @@ describe('recognize', async () => {
expect(c.length).toEqual(1);
checkActivatedRoute(c[0], 'b', {}, ComponentB);
});
it('should set url segment and index properly', async () => {
const url = tree('a/b') as any;
const s = await recognize(
[{
path: 'a',
component: ComponentA,
children: [
{path: 'b', component: ComponentB}, {path: '', component: ComponentC, outlet: 'aux'}
]
}],
'a/b');
expect((s.root as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((s.root as any)._lastPathIndex).toBe(-1);
const a = s.root.firstChild!;
expect((a as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((a as any)._lastPathIndex).toBe(0);
const b = a.firstChild!;
expect((b as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((b as any)._lastPathIndex).toBe(1);
const c = a.children[1];
expect((c as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((c as any)._lastPathIndex).toBe(0);
});
it('should set url segment and index properly when nested empty-path segments', async () => {
const url = tree('a') as any;
const s = await recognize(
[{
path: 'a',
children:
[{path: '', component: ComponentB, children: [{path: '', component: ComponentC}]}]
}],
'a');
expect((s.root as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((s.root as any)._lastPathIndex).toBe(-1);
const a = s.root.firstChild!;
expect((a as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((a as any)._lastPathIndex).toBe(0);
const b = a.firstChild!;
expect((b as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((b as any)._lastPathIndex).toBe(0);
const c = b.firstChild!;
expect((c as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((c as any)._lastPathIndex).toBe(0);
});
it('should set url segment and index properly for nested empty-path segments', async () => {
const url = tree('a/b');
const s = await recognize(
[{
path: 'a',
children: [{
path: 'b',
component: ComponentB,
children: [
{path: '', component: ComponentC, children: [{path: '', component: ComponentD}]}
]
}]
}],
'a/b', 'emptyOnly');
expect((s.root as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((s.root as any)._lastPathIndex).toBe(-1);
const a = s.root.firstChild!;
expect((a as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((a as any)._lastPathIndex).toBe(0);
const b = a.firstChild!;
expect((b as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((b as any)._lastPathIndex).toBe(1);
const c = b.firstChild!;
expect((c as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((c as any)._lastPathIndex).toBe(1);
const d = c.firstChild!;
expect((d as any)._urlSegment.toString())
.toEqual(url.root.children[PRIMARY_OUTLET].toString());
expect((d as any)._lastPathIndex).toBe(1);
});
it('should set url segment and index properly when nested empty-path segments (2)',
async () => {
const url = tree('');
const s = await recognize(
[{
path: '',
children: [
{path: '', component: ComponentB, children: [{path: '', component: ComponentC}]}
]
}],
'');
expect((s.root as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((s.root as any)._lastPathIndex).toBe(-1);
const a = s.root.firstChild!;
expect((a as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((a as any)._lastPathIndex).toBe(-1);
const b = a.firstChild!;
expect((b as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((b as any)._lastPathIndex).toBe(-1);
const c = b.firstChild!;
expect((c as any)._urlSegment.toString()).toEqual(url.root.toString());
expect((c as any)._lastPathIndex).toBe(-1);
});
});
describe('aux split at the end (no right child)', async () => {