feat(router): Add more find-tuned control in `routerLinkActiveOptions` (#40303)
This commit adds more configurability to the `Router#isActive` method and `RouterLinkActive#routerLinkActiveOptions`. It allows tuning individual match options for query params and the url tree, which were either both partial or both exact matches in the past. Additionally, it also allows matching against the fragment and matrix parameters. fixes #13205 BREAKING CHANGE: The type of the `RouterLinkActive.routerLinkActiveOptions` input was expanded to allow more fine-tuned control. Code that previously read this property may need to be updated to account for the new type. PR Close #40303
This commit is contained in:
parent
29d8a0ab09
commit
6c05c80f19
|
@ -158,6 +158,13 @@ export declare class GuardsCheckStart extends RouterEvent {
|
||||||
|
|
||||||
export declare type InitialNavigation = 'disabled' | 'enabled' | 'enabledBlocking' | 'enabledNonBlocking';
|
export declare type InitialNavigation = 'disabled' | 'enabled' | 'enabledBlocking' | 'enabledNonBlocking';
|
||||||
|
|
||||||
|
export declare interface IsActiveMatchOptions {
|
||||||
|
fragment: 'exact' | 'ignored';
|
||||||
|
matrixParams: 'exact' | 'subset' | 'ignored';
|
||||||
|
paths: 'exact' | 'subset';
|
||||||
|
queryParams: 'exact' | 'subset' | 'ignored';
|
||||||
|
}
|
||||||
|
|
||||||
export declare type LoadChildren = LoadChildrenCallback | DeprecatedLoadChildren;
|
export declare type LoadChildren = LoadChildrenCallback | DeprecatedLoadChildren;
|
||||||
|
|
||||||
export declare type LoadChildrenCallback = () => Type<any> | NgModuleFactory<any> | Observable<Type<any>> | Promise<NgModuleFactory<any> | Type<any> | any>;
|
export declare type LoadChildrenCallback = () => Type<any> | NgModuleFactory<any> | Observable<Type<any>> | Promise<NgModuleFactory<any> | Type<any> | any>;
|
||||||
|
@ -345,7 +352,8 @@ export declare class Router {
|
||||||
dispose(): void;
|
dispose(): void;
|
||||||
getCurrentNavigation(): Navigation | null;
|
getCurrentNavigation(): Navigation | null;
|
||||||
initialNavigation(): void;
|
initialNavigation(): void;
|
||||||
isActive(url: string | UrlTree, exact: boolean): boolean;
|
/** @deprecated */ isActive(url: string | UrlTree, exact: boolean): boolean;
|
||||||
|
isActive(url: string | UrlTree, matchOptions: IsActiveMatchOptions): boolean;
|
||||||
navigate(commands: any[], extras?: NavigationExtras): Promise<boolean>;
|
navigate(commands: any[], extras?: NavigationExtras): Promise<boolean>;
|
||||||
navigateByUrl(url: string | UrlTree, extras?: NavigationBehaviorOptions): Promise<boolean>;
|
navigateByUrl(url: string | UrlTree, extras?: NavigationBehaviorOptions): Promise<boolean>;
|
||||||
ngOnDestroy(): void;
|
ngOnDestroy(): void;
|
||||||
|
@ -400,7 +408,7 @@ export declare class RouterLinkActive implements OnChanges, OnDestroy, AfterCont
|
||||||
set routerLinkActive(data: string[] | string);
|
set routerLinkActive(data: string[] | string);
|
||||||
routerLinkActiveOptions: {
|
routerLinkActiveOptions: {
|
||||||
exact: boolean;
|
exact: boolean;
|
||||||
};
|
} | IsActiveMatchOptions;
|
||||||
constructor(router: Router, element: ElementRef, renderer: Renderer2, cdr: ChangeDetectorRef, link?: RouterLink | undefined, linkWithHref?: RouterLinkWithHref | undefined);
|
constructor(router: Router, element: ElementRef, renderer: Renderer2, cdr: ChangeDetectorRef, link?: RouterLink | undefined, linkWithHref?: RouterLinkWithHref | undefined);
|
||||||
ngAfterContentInit(): void;
|
ngAfterContentInit(): void;
|
||||||
ngOnChanges(changes: SimpleChanges): void;
|
ngOnChanges(changes: SimpleChanges): void;
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 2285,
|
"runtime-es2015": 2285,
|
||||||
"main-es2015": 241843,
|
"main-es2015": 242531,
|
||||||
"polyfills-es2015": 36709,
|
"polyfills-es2015": 36709,
|
||||||
"5-es2015": 745
|
"5-es2015": 745
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 2289,
|
"runtime-es2015": 2289,
|
||||||
"main-es2015": 217591,
|
"main-es2015": 218317,
|
||||||
"polyfills-es2015": 36723,
|
"polyfills-es2015": 36723,
|
||||||
"5-es2015": 781
|
"5-es2015": 781
|
||||||
}
|
}
|
||||||
|
|
|
@ -1199,6 +1199,9 @@
|
||||||
{
|
{
|
||||||
"name": "equalPath"
|
"name": "equalPath"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "exactMatchOptions"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "executeCheckHooks"
|
"name": "executeCheckHooks"
|
||||||
},
|
},
|
||||||
|
@ -1661,6 +1664,9 @@
|
||||||
{
|
{
|
||||||
"name": "materializeViewResults"
|
"name": "materializeViewResults"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "matrixParamsMatch"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "maybeUnwrapFn"
|
"name": "maybeUnwrapFn"
|
||||||
},
|
},
|
||||||
|
@ -1754,6 +1760,12 @@
|
||||||
{
|
{
|
||||||
"name": "optionsReducer"
|
"name": "optionsReducer"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "paramCompareMap"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pathCompareMap"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "pipeFromArray"
|
"name": "pipeFromArray"
|
||||||
},
|
},
|
||||||
|
@ -1949,6 +1961,9 @@
|
||||||
{
|
{
|
||||||
"name": "subscribeToResult"
|
"name": "subscribeToResult"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "subsetMatchOptions"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "supportsState"
|
"name": "supportsState"
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {mergeAll} from 'rxjs/operators';
|
||||||
|
|
||||||
import {Event, NavigationEnd} from '../events';
|
import {Event, NavigationEnd} from '../events';
|
||||||
import {Router} from '../router';
|
import {Router} from '../router';
|
||||||
|
import {IsActiveMatchOptions} from '../url_tree';
|
||||||
|
|
||||||
import {RouterLink, RouterLinkWithHref} from './router_link';
|
import {RouterLink, RouterLinkWithHref} from './router_link';
|
||||||
|
|
||||||
|
@ -89,7 +90,15 @@ export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit
|
||||||
private linkInputChangesSubscription?: Subscription;
|
private linkInputChangesSubscription?: Subscription;
|
||||||
public readonly isActive: boolean = false;
|
public readonly isActive: boolean = false;
|
||||||
|
|
||||||
@Input() routerLinkActiveOptions: {exact: boolean} = {exact: false};
|
/**
|
||||||
|
* Options to configure how to determine if the router link is active.
|
||||||
|
*
|
||||||
|
* These options are passed to the `Router.isActive()` function.
|
||||||
|
*
|
||||||
|
* @see Router.isActive
|
||||||
|
*/
|
||||||
|
@Input() routerLinkActiveOptions: {exact: boolean}|IsActiveMatchOptions = {exact: false};
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router, private element: ElementRef, private renderer: Renderer2,
|
private router: Router, private element: ElementRef, private renderer: Renderer2,
|
||||||
|
@ -159,8 +168,11 @@ export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit
|
||||||
}
|
}
|
||||||
|
|
||||||
private isLinkActive(router: Router): (link: (RouterLink|RouterLinkWithHref)) => boolean {
|
private isLinkActive(router: Router): (link: (RouterLink|RouterLinkWithHref)) => boolean {
|
||||||
return (link: RouterLink|RouterLinkWithHref) =>
|
const options = 'paths' in this.routerLinkActiveOptions ?
|
||||||
router.isActive(link.urlTree, this.routerLinkActiveOptions.exact);
|
this.routerLinkActiveOptions :
|
||||||
|
// While the types should disallow `undefined` here, it's possible without strict inputs
|
||||||
|
(this.routerLinkActiveOptions.exact || false);
|
||||||
|
return (link: RouterLink|RouterLinkWithHref) => router.isActive(link.urlTree, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private hasActiveLinks(): boolean {
|
private hasActiveLinks(): boolean {
|
||||||
|
@ -169,4 +181,4 @@ export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit
|
||||||
this.linkWithHref && isActiveCheckFn(this.linkWithHref) ||
|
this.linkWithHref && isActiveCheckFn(this.linkWithHref) ||
|
||||||
this.links.some(isActiveCheckFn) || this.linksWithHrefs.some(isActiveCheckFn);
|
this.links.some(isActiveCheckFn) || this.linksWithHrefs.some(isActiveCheckFn);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,7 +22,7 @@ export {NoPreloading, PreloadAllModules, PreloadingStrategy, RouterPreloader} fr
|
||||||
export {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot} from './router_state';
|
export {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot} from './router_state';
|
||||||
export {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET} from './shared';
|
export {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET} from './shared';
|
||||||
export {UrlHandlingStrategy} from './url_handling_strategy';
|
export {UrlHandlingStrategy} from './url_handling_strategy';
|
||||||
export {DefaultUrlSerializer, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
|
export {DefaultUrlSerializer, IsActiveMatchOptions, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
|
||||||
export {VERSION} from './version';
|
export {VERSION} from './version';
|
||||||
|
|
||||||
export * from './private_export';
|
export * from './private_export';
|
||||||
|
|
|
@ -27,7 +27,7 @@ import {ChildrenOutletContexts} from './router_outlet_context';
|
||||||
import {ActivatedRoute, createEmptyState, RouterState, RouterStateSnapshot} from './router_state';
|
import {ActivatedRoute, createEmptyState, RouterState, RouterStateSnapshot} from './router_state';
|
||||||
import {isNavigationCancelingError, navigationCancelingError, Params} from './shared';
|
import {isNavigationCancelingError, navigationCancelingError, Params} from './shared';
|
||||||
import {DefaultUrlHandlingStrategy, UrlHandlingStrategy} from './url_handling_strategy';
|
import {DefaultUrlHandlingStrategy, UrlHandlingStrategy} from './url_handling_strategy';
|
||||||
import {containsTree, createEmptyUrlTree, UrlSerializer, UrlTree} from './url_tree';
|
import {containsTree, createEmptyUrlTree, IsActiveMatchOptions, UrlSerializer, UrlTree} from './url_tree';
|
||||||
import {standardizeConfig, validateConfig} from './utils/config';
|
import {standardizeConfig, validateConfig} from './utils/config';
|
||||||
import {Checks, getAllRouteGuards} from './utils/preactivation';
|
import {Checks, getAllRouteGuards} from './utils/preactivation';
|
||||||
import {isUrlTree} from './utils/type_guards';
|
import {isUrlTree} from './utils/type_guards';
|
||||||
|
@ -356,6 +356,29 @@ type LocationChangeInfo = {
|
||||||
transitionId: number
|
transitionId: number
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The equivalent `IsActiveUrlTreeOptions` options for `Router.isActive` is called with `false`
|
||||||
|
* (exact = true).
|
||||||
|
*/
|
||||||
|
export const exactMatchOptions: IsActiveMatchOptions = {
|
||||||
|
paths: 'exact',
|
||||||
|
fragment: 'ignored',
|
||||||
|
matrixParams: 'ignored',
|
||||||
|
queryParams: 'exact'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The equivalent `IsActiveUrlTreeOptions` options for `Router.isActive` is called with `false`
|
||||||
|
* (exact = false).
|
||||||
|
*/
|
||||||
|
export const subsetMatchOptions: IsActiveMatchOptions = {
|
||||||
|
paths: 'subset',
|
||||||
|
fragment: 'ignored',
|
||||||
|
matrixParams: 'ignored',
|
||||||
|
queryParams: 'subset'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
|
@ -1213,14 +1236,39 @@ export class Router {
|
||||||
return urlTree;
|
return urlTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether the url is activated */
|
/**
|
||||||
isActive(url: string|UrlTree, exact: boolean): boolean {
|
* Returns whether the url is activated.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
* Use `IsActiveUrlTreeOptions` instead.
|
||||||
|
*
|
||||||
|
* - The equivalent `IsActiveUrlTreeOptions` for `true` is
|
||||||
|
* `{paths: 'exact', queryParams: 'exact', fragment: 'ignored', matrixParams: 'ignored'}`.
|
||||||
|
* - The equivalent for `false` is
|
||||||
|
* `{paths: 'subset', queryParams: 'subset', fragment: 'ignored', matrixParams: 'ignored'}`.
|
||||||
|
*/
|
||||||
|
isActive(url: string|UrlTree, exact: boolean): boolean;
|
||||||
|
/**
|
||||||
|
* Returns whether the url is activated.
|
||||||
|
*/
|
||||||
|
isActive(url: string|UrlTree, matchOptions: IsActiveMatchOptions): boolean;
|
||||||
|
/** @internal */
|
||||||
|
isActive(url: string|UrlTree, matchOptions: boolean|IsActiveMatchOptions): boolean;
|
||||||
|
isActive(url: string|UrlTree, matchOptions: boolean|IsActiveMatchOptions): boolean {
|
||||||
|
let options: IsActiveMatchOptions;
|
||||||
|
if (matchOptions === true) {
|
||||||
|
options = {...exactMatchOptions};
|
||||||
|
} else if (matchOptions === false) {
|
||||||
|
options = {...subsetMatchOptions};
|
||||||
|
} else {
|
||||||
|
options = matchOptions;
|
||||||
|
}
|
||||||
if (isUrlTree(url)) {
|
if (isUrlTree(url)) {
|
||||||
return containsTree(this.currentUrlTree, url, exact);
|
return containsTree(this.currentUrlTree, url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlTree = this.parseUrl(url);
|
const urlTree = this.parseUrl(url);
|
||||||
return containsTree(this.currentUrlTree, urlTree, exact);
|
return containsTree(this.currentUrlTree, urlTree, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeEmptyProps(params: Params): Params {
|
private removeEmptyProps(params: Params): Params {
|
||||||
|
|
|
@ -13,53 +13,128 @@ export function createEmptyUrlTree() {
|
||||||
return new UrlTree(new UrlSegmentGroup([], {}), {}, null);
|
return new UrlTree(new UrlSegmentGroup([], {}), {}, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function containsTree(container: UrlTree, containee: UrlTree, exact: boolean): boolean {
|
/**
|
||||||
if (exact) {
|
* A set of options which specify how to determine if a `UrlTree` is active, given the `UrlTree`
|
||||||
return equalQueryParams(container.queryParams, containee.queryParams) &&
|
* for the current router state.
|
||||||
equalSegmentGroups(container.root, containee.root);
|
*
|
||||||
}
|
* @publicApi
|
||||||
|
* @see Router.isActive
|
||||||
return containsQueryParams(container.queryParams, containee.queryParams) &&
|
*/
|
||||||
containsSegmentGroup(container.root, containee.root);
|
export interface IsActiveMatchOptions {
|
||||||
|
/**
|
||||||
|
* Defines the strategy for comparing the matrix parameters of two `UrlTree`s.
|
||||||
|
*
|
||||||
|
* The matrix parameter matching is dependent on the strategy for matching the
|
||||||
|
* segments. That is, if the `paths` option is set to `'subset'`, only
|
||||||
|
* the matrix parameters of the matching segments will be compared.
|
||||||
|
*
|
||||||
|
* - `'exact'`: Requires that matching segments also have exact matrix parameter
|
||||||
|
* matches.
|
||||||
|
* - `'subset'`: The matching segments in the router's active `UrlTree` may contain
|
||||||
|
* extra matrix parameters, but those that exist in the `UrlTree` in question must match.
|
||||||
|
* - `'ignored'`: When comparing `UrlTree`s, matrix params will be ignored.
|
||||||
|
*/
|
||||||
|
matrixParams: 'exact'|'subset'|'ignored';
|
||||||
|
/**
|
||||||
|
* Defines the strategy for comparing the query parameters of two `UrlTree`s.
|
||||||
|
*
|
||||||
|
* - `'exact'`: the query parameters must match exactly.
|
||||||
|
* - `'subset'`: the active `UrlTree` may contain extra parameters,
|
||||||
|
* but must match the key and value of any that exist in the `UrlTree` in question.
|
||||||
|
* - `'ignored'`: When comparing `UrlTree`s, query params will be ignored.
|
||||||
|
*/
|
||||||
|
queryParams: 'exact'|'subset'|'ignored';
|
||||||
|
/**
|
||||||
|
* Defines the strategy for comparing the `UrlSegment`s of the `UrlTree`s.
|
||||||
|
*
|
||||||
|
* - `'exact'`: all segments in each `UrlTree` must match.
|
||||||
|
* - `'subset'`: a `UrlTree` will be determined to be active if it
|
||||||
|
* is a subtree of the active route. That is, the active route may contain extra
|
||||||
|
* segments, but must at least have all the segements of the `UrlTree` in question.
|
||||||
|
*/
|
||||||
|
paths: 'exact'|'subset';
|
||||||
|
/**
|
||||||
|
* - 'exact'`: indicates that the `UrlTree` fragments must be equal.
|
||||||
|
* - `'ignored'`: the fragments will not be compared when determining if a
|
||||||
|
* `UrlTree` is active.
|
||||||
|
*/
|
||||||
|
fragment: 'exact'|'ignored';
|
||||||
}
|
}
|
||||||
|
|
||||||
function equalQueryParams(container: Params, containee: Params): boolean {
|
type ParamMatchOptions = 'exact'|'subset'|'ignored';
|
||||||
|
|
||||||
|
type PathCompareFn =
|
||||||
|
(container: UrlSegmentGroup, containee: UrlSegmentGroup, matrixParams: ParamMatchOptions) =>
|
||||||
|
boolean;
|
||||||
|
type ParamCompareFn = (container: Params, containee: Params) => boolean;
|
||||||
|
|
||||||
|
const pathCompareMap: Record<IsActiveMatchOptions['paths'], PathCompareFn> = {
|
||||||
|
'exact': equalSegmentGroups,
|
||||||
|
'subset': containsSegmentGroup,
|
||||||
|
};
|
||||||
|
const paramCompareMap: Record<ParamMatchOptions, ParamCompareFn> = {
|
||||||
|
'exact': equalParams,
|
||||||
|
'subset': containsParams,
|
||||||
|
'ignored': () => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function containsTree(
|
||||||
|
container: UrlTree, containee: UrlTree, options: IsActiveMatchOptions): boolean {
|
||||||
|
return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) &&
|
||||||
|
paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) &&
|
||||||
|
!(options.fragment === 'exact' && container.fragment !== containee.fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
function equalParams(container: Params, containee: Params): boolean {
|
||||||
// TODO: This does not handle array params correctly.
|
// TODO: This does not handle array params correctly.
|
||||||
return shallowEqual(container, containee);
|
return shallowEqual(container, containee);
|
||||||
}
|
}
|
||||||
|
|
||||||
function equalSegmentGroups(container: UrlSegmentGroup, containee: UrlSegmentGroup): boolean {
|
function equalSegmentGroups(
|
||||||
|
container: UrlSegmentGroup, containee: UrlSegmentGroup,
|
||||||
|
matrixParams: ParamMatchOptions): boolean {
|
||||||
if (!equalPath(container.segments, containee.segments)) return false;
|
if (!equalPath(container.segments, containee.segments)) return false;
|
||||||
|
if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (container.numberOfChildren !== containee.numberOfChildren) return false;
|
if (container.numberOfChildren !== containee.numberOfChildren) return false;
|
||||||
for (const c in containee.children) {
|
for (const c in containee.children) {
|
||||||
if (!container.children[c]) return false;
|
if (!container.children[c]) return false;
|
||||||
if (!equalSegmentGroups(container.children[c], containee.children[c])) return false;
|
if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function containsQueryParams(container: Params, containee: Params): boolean {
|
function containsParams(container: Params, containee: Params): boolean {
|
||||||
return Object.keys(containee).length <= Object.keys(container).length &&
|
return Object.keys(containee).length <= Object.keys(container).length &&
|
||||||
Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key]));
|
Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function containsSegmentGroup(container: UrlSegmentGroup, containee: UrlSegmentGroup): boolean {
|
function containsSegmentGroup(
|
||||||
return containsSegmentGroupHelper(container, containee, containee.segments);
|
container: UrlSegmentGroup, containee: UrlSegmentGroup,
|
||||||
|
matrixParams: ParamMatchOptions): boolean {
|
||||||
|
return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
function containsSegmentGroupHelper(
|
function containsSegmentGroupHelper(
|
||||||
container: UrlSegmentGroup, containee: UrlSegmentGroup, containeePaths: UrlSegment[]): boolean {
|
container: UrlSegmentGroup, containee: UrlSegmentGroup, containeePaths: UrlSegment[],
|
||||||
|
matrixParams: ParamMatchOptions): boolean {
|
||||||
if (container.segments.length > containeePaths.length) {
|
if (container.segments.length > containeePaths.length) {
|
||||||
const current = container.segments.slice(0, containeePaths.length);
|
const current = container.segments.slice(0, containeePaths.length);
|
||||||
if (!equalPath(current, containeePaths)) return false;
|
if (!equalPath(current, containeePaths)) return false;
|
||||||
if (containee.hasChildren()) return false;
|
if (containee.hasChildren()) return false;
|
||||||
|
if (!matrixParamsMatch(current, containeePaths, matrixParams)) return false;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else if (container.segments.length === containeePaths.length) {
|
} else if (container.segments.length === containeePaths.length) {
|
||||||
if (!equalPath(container.segments, containeePaths)) return false;
|
if (!equalPath(container.segments, containeePaths)) return false;
|
||||||
|
if (!matrixParamsMatch(container.segments, containeePaths, matrixParams)) return false;
|
||||||
for (const c in containee.children) {
|
for (const c in containee.children) {
|
||||||
if (!container.children[c]) return false;
|
if (!container.children[c]) return false;
|
||||||
if (!containsSegmentGroup(container.children[c], containee.children[c])) return false;
|
if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -67,11 +142,20 @@ function containsSegmentGroupHelper(
|
||||||
const current = containeePaths.slice(0, container.segments.length);
|
const current = containeePaths.slice(0, container.segments.length);
|
||||||
const next = containeePaths.slice(container.segments.length);
|
const next = containeePaths.slice(container.segments.length);
|
||||||
if (!equalPath(container.segments, current)) return false;
|
if (!equalPath(container.segments, current)) return false;
|
||||||
|
if (!matrixParamsMatch(container.segments, current, matrixParams)) return false;
|
||||||
if (!container.children[PRIMARY_OUTLET]) return false;
|
if (!container.children[PRIMARY_OUTLET]) return false;
|
||||||
return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next);
|
return containsSegmentGroupHelper(
|
||||||
|
container.children[PRIMARY_OUTLET], containee, next, matrixParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function matrixParamsMatch(
|
||||||
|
containerPaths: UrlSegment[], containeePaths: UrlSegment[], options: ParamMatchOptions) {
|
||||||
|
return containeePaths.every((containeeSegment, i) => {
|
||||||
|
return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {exactMatchOptions, subsetMatchOptions} from '../src/router';
|
||||||
import {containsTree, DefaultUrlSerializer} from '../src/url_tree';
|
import {containsTree, DefaultUrlSerializer} from '../src/url_tree';
|
||||||
|
|
||||||
describe('UrlTree', () => {
|
describe('UrlTree', () => {
|
||||||
|
@ -33,80 +34,80 @@ describe('UrlTree', () => {
|
||||||
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, exactMatchOptions)).toBe(true);
|
||||||
expect(containsTree(t2, t1, true)).toBe(true);
|
expect(containsTree(t2, t1, exactMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when queryParams are the same', () => {
|
it('should return true when queryParams are the same', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=1&page=5');
|
const t1 = serializer.parse('/one/two?test=1&page=5');
|
||||||
const t2 = serializer.parse('/one/two?test=1&page=5');
|
const t2 = serializer.parse('/one/two?test=1&page=5');
|
||||||
expect(containsTree(t1, t2, true)).toBe(true);
|
expect(containsTree(t1, t2, exactMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when queryParams are the same but with diffrent order', () => {
|
it('should return true when queryParams are the same but with diffrent order', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=1&page=5');
|
const t1 = serializer.parse('/one/two?test=1&page=5');
|
||||||
const t2 = serializer.parse('/one/two?page=5&test=1');
|
const t2 = serializer.parse('/one/two?page=5&test=1');
|
||||||
expect(containsTree(t1, t2, true)).toBe(true);
|
expect(containsTree(t1, t2, exactMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when queryParams contains array params that are the same', () => {
|
it('should return true when queryParams contains array params that are the same', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
const t1 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
||||||
const t2 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
const t2 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
||||||
expect(containsTree(t1, t2, true)).toBe(true);
|
expect(containsTree(t1, t2, exactMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when queryParams contains array params but are not the same', () => {
|
it('should return false when queryParams contains array params but are not the same', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
const t1 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
||||||
const t2 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=7');
|
const t2 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=7');
|
||||||
expect(containsTree(t1, t2, false)).toBe(false);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when queryParams are not the same', () => {
|
it('should return false when queryParams are not the same', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=1&page=5');
|
const t1 = serializer.parse('/one/two?test=1&page=5');
|
||||||
const t2 = serializer.parse('/one/two?test=1');
|
const t2 = serializer.parse('/one/two?test=1');
|
||||||
expect(containsTree(t1, t2, true)).toBe(false);
|
expect(containsTree(t1, t2, exactMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when queryParams are not the same', () => {
|
it('should return false when queryParams are not the same', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=4&test=4&test=2');
|
const t1 = serializer.parse('/one/two?test=4&test=4&test=2');
|
||||||
const t2 = serializer.parse('/one/two?test=4&test=3&test=2');
|
const t2 = serializer.parse('/one/two?test=4&test=3&test=2');
|
||||||
expect(containsTree(t1, t2, false)).toBe(false);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when queryParams are the same in different order', () => {
|
it('should return true when queryParams are the same in different order', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=4&test=3&test=2');
|
const t1 = serializer.parse('/one/two?test=4&test=3&test=2');
|
||||||
const t2 = serializer.parse('/one/two?test=2&test=3&test=4');
|
const t2 = serializer.parse('/one/two?test=2&test=3&test=4');
|
||||||
expect(containsTree(t1, t2, false)).toBe(true);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when queryParams are the same in different order', () => {
|
it('should return true when queryParams are the same in different order', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=4&test=4&test=1');
|
const t1 = serializer.parse('/one/two?test=4&test=4&test=1');
|
||||||
const t2 = serializer.parse('/one/two?test=1&test=4&test=4');
|
const t2 = serializer.parse('/one/two?test=1&test=4&test=4');
|
||||||
expect(containsTree(t1, t2, false)).toBe(true);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when containee is missing queryParams', () => {
|
it('should return false when containee is missing queryParams', () => {
|
||||||
const t1 = serializer.parse('/one/two?page=5');
|
const t1 = serializer.parse('/one/two?page=5');
|
||||||
const t2 = serializer.parse('/one/two');
|
const t2 = serializer.parse('/one/two');
|
||||||
expect(containsTree(t1, t2, true)).toBe(false);
|
expect(containsTree(t1, t2, exactMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
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, exactMatchOptions)).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, exactMatchOptions)).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, exactMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -114,85 +115,183 @@ describe('UrlTree', () => {
|
||||||
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, subsetMatchOptions)).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, subsetMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true container has its paths split into multiple segments', () => {
|
it('should return true container has its paths split 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, subsetMatchOptions)).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, subsetMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false containee has segments that the container does not have', () => {
|
it('should return false 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, subsetMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when containee has extra paths', () => {
|
it('should return false when containee has extra paths', () => {
|
||||||
const t1 = serializer.parse('/one');
|
const t1 = serializer.parse('/one');
|
||||||
const t2 = serializer.parse('/one/two');
|
const t2 = serializer.parse('/one/two');
|
||||||
expect(containsTree(t1, t2, false)).toBe(false);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when queryParams are the same', () => {
|
it('should return true when queryParams are the same', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=1&page=5');
|
const t1 = serializer.parse('/one/two?test=1&page=5');
|
||||||
const t2 = serializer.parse('/one/two?test=1&page=5');
|
const t2 = serializer.parse('/one/two?test=1&page=5');
|
||||||
expect(containsTree(t1, t2, false)).toBe(true);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when container contains containees queryParams', () => {
|
it('should return true when container contains containees queryParams', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=1&u=5');
|
const t1 = serializer.parse('/one/two?test=1&u=5');
|
||||||
const t2 = serializer.parse('/one/two?u=5');
|
const t2 = serializer.parse('/one/two?u=5');
|
||||||
expect(containsTree(t1, t2, false)).toBe(true);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when containee does not have queryParams', () => {
|
it('should return true when containee does not have queryParams', () => {
|
||||||
const t1 = serializer.parse('/one/two?page=5');
|
const t1 = serializer.parse('/one/two?page=5');
|
||||||
const t2 = serializer.parse('/one/two');
|
const t2 = serializer.parse('/one/two');
|
||||||
expect(containsTree(t1, t2, false)).toBe(true);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when containee has but container does not have queryParams', () => {
|
it('should return false when containee has but container does not have queryParams', () => {
|
||||||
const t1 = serializer.parse('/one/two');
|
const t1 = serializer.parse('/one/two');
|
||||||
const t2 = serializer.parse('/one/two?page=1');
|
const t2 = serializer.parse('/one/two?page=1');
|
||||||
expect(containsTree(t1, t2, false)).toBe(false);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true when container has array params but containee does not have', () => {
|
it('should return true when container has array params but containee does not have', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
const t1 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
||||||
const t2 = serializer.parse('/one/two?test=a&test=b');
|
const t2 = serializer.parse('/one/two?test=a&test=b');
|
||||||
expect(containsTree(t1, t2, false)).toBe(true);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when containee has array params but container does not have', () => {
|
it('should return false when containee has array params but container does not have', () => {
|
||||||
const t1 = serializer.parse('/one/two?test=a&test=b');
|
const t1 = serializer.parse('/one/two?test=a&test=b');
|
||||||
const t2 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
const t2 = serializer.parse('/one/two?test=a&test=b&pages=5&pages=6');
|
||||||
expect(containsTree(t1, t2, false)).toBe(false);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when containee has different queryParams', () => {
|
it('should return false when containee has different queryParams', () => {
|
||||||
const t1 = serializer.parse('/one/two?page=5');
|
const t1 = serializer.parse('/one/two?page=5');
|
||||||
const t2 = serializer.parse('/one/two?test=1');
|
const t2 = serializer.parse('/one/two?test=1');
|
||||||
expect(containsTree(t1, t2, false)).toBe(false);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when containee has more queryParams than container', () => {
|
it('should return false when containee has more queryParams than container', () => {
|
||||||
const t1 = serializer.parse('/one/two?page=5');
|
const t1 = serializer.parse('/one/two?page=5');
|
||||||
const t2 = serializer.parse('/one/two?page=5&test=1');
|
const t2 = serializer.parse('/one/two?page=5&test=1');
|
||||||
expect(containsTree(t1, t2, false)).toBe(false);
|
expect(containsTree(t1, t2, subsetMatchOptions)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ignored query params', () => {
|
||||||
|
it('should return true when queryParams differ but are ignored', () => {
|
||||||
|
const t1 = serializer.parse('/?test=1&page=2');
|
||||||
|
const t2 = serializer.parse('/?test=3&page=4&x=y');
|
||||||
|
expect(containsTree(t1, t2, {...exactMatchOptions, queryParams: 'ignored'})).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fragment', () => {
|
||||||
|
it('should return false when fragments differ but options require exact match', () => {
|
||||||
|
const t1 = serializer.parse('/#fragment1');
|
||||||
|
const t2 = serializer.parse('/#fragment2');
|
||||||
|
expect(containsTree(t1, t2, {...exactMatchOptions, fragment: 'exact'})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when fragments differ but options ignore the fragment', () => {
|
||||||
|
const t1 = serializer.parse('/#fragment1');
|
||||||
|
const t2 = serializer.parse('/#fragment2');
|
||||||
|
expect(containsTree(t1, t2, {...exactMatchOptions, fragment: 'ignored'})).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('matrix params', () => {
|
||||||
|
describe('ignored', () => {
|
||||||
|
it('returns true when matrix params differ but are ignored', () => {
|
||||||
|
const t1 = serializer.parse('/a;id=15;foo=foo');
|
||||||
|
const t2 = serializer.parse('/a;abc=123');
|
||||||
|
expect(containsTree(t1, t2, {...exactMatchOptions, matrixParams: 'ignored'})).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('exact match', () => {
|
||||||
|
const matrixParams = 'exact';
|
||||||
|
|
||||||
|
it('returns true when matrix params match', () => {
|
||||||
|
const t1 = serializer.parse('/a;id=15;foo=foo');
|
||||||
|
const t2 = serializer.parse('/a;id=15;foo=foo');
|
||||||
|
expect(containsTree(t1, t2, {...exactMatchOptions, matrixParams})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false when matrix params differ', () => {
|
||||||
|
const t1 = serializer.parse('/a;id=15;foo=foo');
|
||||||
|
const t2 = serializer.parse('/a;abc=123');
|
||||||
|
expect(containsTree(t1, t2, {...exactMatchOptions, matrixParams})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true when matrix params match on the subset of the matched url tree', () => {
|
||||||
|
const t1 = serializer.parse('/a;id=15;foo=bar/c');
|
||||||
|
const t2 = serializer.parse('/a;id=15;foo=bar');
|
||||||
|
expect(containsTree(t1, t2, {...subsetMatchOptions, matrixParams})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when matrix params match on subset of urlTree match ' +
|
||||||
|
'with container paths split into multiple segments',
|
||||||
|
() => {
|
||||||
|
const t1 = serializer.parse('/one;a=1/(two;b=2//left:three)');
|
||||||
|
const t2 = serializer.parse('/one;a=1/two;b=2');
|
||||||
|
expect(containsTree(t1, t2, {...subsetMatchOptions, matrixParams})).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('subset match', () => {
|
||||||
|
const matrixParams = 'subset';
|
||||||
|
|
||||||
|
it('returns true when matrix params match', () => {
|
||||||
|
const t1 = serializer.parse('/a;id=15;foo=foo');
|
||||||
|
const t2 = serializer.parse('/a;id=15;foo=foo');
|
||||||
|
expect(containsTree(t1, t2, {...exactMatchOptions, matrixParams})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true when container has extra matrix params', () => {
|
||||||
|
const t1 = serializer.parse('/a;id=15;foo=foo');
|
||||||
|
const t2 = serializer.parse('/a;id=15');
|
||||||
|
expect(containsTree(t1, t2, {...exactMatchOptions, matrixParams})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false when matrix params differ', () => {
|
||||||
|
const t1 = serializer.parse('/a;id=15;foo=foo');
|
||||||
|
const t2 = serializer.parse('/a;abc=123');
|
||||||
|
expect(containsTree(t1, t2, {...exactMatchOptions, matrixParams})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true when matrix params match on the subset of the matched url tree', () => {
|
||||||
|
const t1 = serializer.parse('/a;id=15;foo=bar/c');
|
||||||
|
const t2 = serializer.parse('/a;id=15;foo=bar');
|
||||||
|
expect(containsTree(t1, t2, {...subsetMatchOptions, matrixParams})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when matrix params match on subset of urlTree match ' +
|
||||||
|
'with container paths split into multiple segments',
|
||||||
|
() => {
|
||||||
|
const t1 = serializer.parse('/one;a=1/(two;b=2//left:three)');
|
||||||
|
const t2 = serializer.parse('/one;a=1/two');
|
||||||
|
expect(containsTree(t1, t2, {...subsetMatchOptions, matrixParams})).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue