mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(router): ambiguous routes
This commit is contained in:
@@ -4,12 +4,7 @@ import { ViewController } from '../view-controller';
|
||||
import { AnimationControllerImpl } from '../../animation-controller/animation-controller';
|
||||
import { createConfigController } from '../../../global/config-controller';
|
||||
|
||||
import {
|
||||
DIRECTION_BACK,
|
||||
DIRECTION_FORWARD,
|
||||
NavOptions,
|
||||
STATE_INITIALIZED,
|
||||
} from '../nav-util';
|
||||
import { NavDirection, NavOptions, ViewState } from '../nav-util';
|
||||
|
||||
|
||||
describe('NavController', () => {
|
||||
@@ -31,7 +26,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(push1Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', undefined, DIRECTION_FORWARD
|
||||
hasCompleted, requiresTransition, 'mock-view1', undefined, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -40,7 +35,7 @@ describe('NavController', () => {
|
||||
await nav.push(mockView(MockView2), null, {animate: false}, push2Done);
|
||||
|
||||
expect(push2Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', DIRECTION_FORWARD
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', NavDirection.forward
|
||||
);
|
||||
|
||||
expect(nav.length()).toEqual(2);
|
||||
@@ -51,7 +46,7 @@ describe('NavController', () => {
|
||||
await nav.push(mockView(MockView3), null, {animate: false}, push3Done);
|
||||
|
||||
expect(push3Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view3', 'mock-view2', DIRECTION_FORWARD
|
||||
hasCompleted, requiresTransition, 'mock-view3', 'mock-view2', NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(3);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -61,7 +56,7 @@ describe('NavController', () => {
|
||||
// Push 4
|
||||
await nav.push(mockView(MockView4), null, {animate: false}, push4Done);
|
||||
expect(push4Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view4', 'mock-view3', DIRECTION_FORWARD
|
||||
hasCompleted, requiresTransition, 'mock-view4', 'mock-view3', NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(4);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -72,7 +67,7 @@ describe('NavController', () => {
|
||||
// Pop 1
|
||||
await nav.pop({animate: false}, pop1Done);
|
||||
expect(pop1Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view3', 'mock-view4', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view3', 'mock-view4', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(3);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -82,7 +77,7 @@ describe('NavController', () => {
|
||||
// Pop 2
|
||||
await nav.pop({animate: false}, pop2Done);
|
||||
expect(pop2Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view3', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view3', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -91,7 +86,7 @@ describe('NavController', () => {
|
||||
// Pop 3
|
||||
await nav.pop({animate: false}, pop3Done);
|
||||
expect(pop3Done).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view2', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view2', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -107,7 +102,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', undefined, DIRECTION_FORWARD
|
||||
hasCompleted, requiresTransition, 'mock-view1', undefined, NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -124,7 +119,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', DIRECTION_FORWARD
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -165,7 +160,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view', 'mock-view', DIRECTION_FORWARD
|
||||
hasCompleted, requiresTransition, 'mock-view', 'mock-view', NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
|
||||
@@ -221,7 +216,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', DIRECTION_FORWARD
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav._views[nav._views.length - 1].component).toEqual(MockView2);
|
||||
@@ -235,7 +230,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', DIRECTION_FORWARD
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view1', NavDirection.forward
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav._views[nav._views.length - 1].component).toEqual(MockView2);
|
||||
@@ -392,7 +387,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view2', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view2', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -416,7 +411,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view3', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view3', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -437,7 +432,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view4', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view4', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -499,7 +494,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view4', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view4', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -563,7 +558,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view4', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view4', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -779,7 +774,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view4', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view4', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -882,7 +877,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view3', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view2', 'mock-view3', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView2);
|
||||
@@ -930,7 +925,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view3', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view1', 'mock-view3', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView1);
|
||||
@@ -957,7 +952,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view4', 'mock-view3', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view4', 'mock-view3', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(1);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView4);
|
||||
@@ -987,7 +982,7 @@ describe('NavController', () => {
|
||||
const hasCompleted = true;
|
||||
const requiresTransition = true;
|
||||
expect(trnsDone).toHaveBeenCalledWith(
|
||||
hasCompleted, requiresTransition, 'mock-view5', 'mock-view2', DIRECTION_BACK
|
||||
hasCompleted, requiresTransition, 'mock-view5', 'mock-view2', NavDirection.back
|
||||
);
|
||||
expect(nav.length()).toEqual(2);
|
||||
expect(nav.getByIndex(0).component).toEqual(MockView4);
|
||||
@@ -1133,7 +1128,7 @@ function mockNavController(): NavControllerBase {
|
||||
? mockElement(enteringView.component) as HTMLElement
|
||||
: enteringView.element = enteringView.component as HTMLElement;
|
||||
}
|
||||
enteringView._state = STATE_INITIALIZED;
|
||||
enteringView._state = ViewState.Initialized;
|
||||
};
|
||||
return nav;
|
||||
}
|
||||
|
||||
@@ -73,11 +73,11 @@ function conferenceAppRouting() {
|
||||
|
||||
|
||||
|
||||
function getRouteIDs(path: string, routes: RouteChain[]): string[] {
|
||||
export function getRouteIDs(path: string, routes: RouteChain[]): string[] {
|
||||
return routerPathToChain(parsePath(path), routes).map(r => r.id);
|
||||
}
|
||||
|
||||
function getRoutePath(ids: RouteID[], routes: RouteChain[]): string {
|
||||
export function getRoutePath(ids: RouteID[], routes: RouteChain[]): string {
|
||||
return generatePath(chainToPath(routerIDsToChain(ids, routes)));
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,44 @@ describe('routerPathToChain', () => {
|
||||
expect(routerPathToChain(['hola', 'adios'], routes)).toEqual(chain4);
|
||||
});
|
||||
|
||||
it('should match the route with higher priority 2', () => {
|
||||
|
||||
const chain1: RouteChain = [{ id: '1', path: ['categories', ':category_slug'], params: undefined }];
|
||||
const chain2: RouteChain = [{ id: '2', path: ['workouts', ':workout_slug'], params: undefined }];
|
||||
const chain3: RouteChain = [{ id: '3', path: ['workouts', ':workout_slug', 'time-select'], params: undefined }];
|
||||
const chain4: RouteChain = [{ id: '4', path: ['workouts', ':workout_slug', 'end-workout'], params: undefined }];
|
||||
const chain5: RouteChain = [{ id: '5', path: ['plans'], params: undefined }];
|
||||
const chain6: RouteChain = [{ id: '6', path: ['custom'], params: undefined }];
|
||||
const chain7: RouteChain = [{ id: '7', path: ['workouts', 'list'], params: undefined }];
|
||||
|
||||
const routes: RouteChain[] = [
|
||||
chain1,
|
||||
chain2,
|
||||
chain3,
|
||||
chain4,
|
||||
chain5,
|
||||
chain6,
|
||||
chain7
|
||||
];
|
||||
// no match
|
||||
expect(routerPathToChain(['categories'], routes)).toEqual(null);
|
||||
expect(routerPathToChain(['workouts'], routes)).toEqual(null);
|
||||
|
||||
expect(routerPathToChain(['plans'], routes)).toEqual(chain5);
|
||||
expect(routerPathToChain(['custom'], routes)).toEqual(chain6);
|
||||
expect(routerPathToChain(['workouts', 'list'], routes)).toEqual(chain7);
|
||||
|
||||
expect(routerPathToChain(['workouts', 'hola'], routes)).toEqual(
|
||||
[{ id: '2', path: ['workouts', ':workout_slug'], params: {'workout_slug': 'hola'} }]
|
||||
);
|
||||
expect(routerPathToChain(['workouts', 'hello', 'time-select'], routes)).toEqual(
|
||||
[{ id: '3', path: ['workouts', ':workout_slug', 'time-select'], params: {'workout_slug': 'hello'} }]
|
||||
);
|
||||
expect(routerPathToChain(['workouts', 'hello2', 'end-workout'], routes)).toEqual(
|
||||
[{ id: '4', path: ['workouts', ':workout_slug', 'end-workout'], params: {'workout_slug': 'hello2'} }]
|
||||
);
|
||||
});
|
||||
|
||||
it('should match the default route', () => {
|
||||
const chain1: RouteChain = [
|
||||
{ id: 'tabs', path: [''], params: undefined },
|
||||
|
||||
@@ -122,8 +122,9 @@ export function routerPathToChain(path: string[], chains: RouteChain[]): RouteCh
|
||||
for (const chain of chains) {
|
||||
const matchedChain = matchesPath(path, chain);
|
||||
if (matchedChain !== null) {
|
||||
if (matchedChain.length > matches) {
|
||||
matches = matchedChain.length;
|
||||
const score = computePriority(matchedChain);
|
||||
if (score > matches) {
|
||||
matches = score;
|
||||
match = matchedChain;
|
||||
}
|
||||
}
|
||||
@@ -131,6 +132,22 @@ export function routerPathToChain(path: string[], chains: RouteChain[]): RouteCh
|
||||
return match;
|
||||
}
|
||||
|
||||
export function computePriority(chain: RouteChain): number {
|
||||
let score = 1;
|
||||
let level = 1;
|
||||
for (const route of chain) {
|
||||
for (const path of route.path) {
|
||||
if (path[0] === ':') {
|
||||
score += Math.pow(1, level);
|
||||
} else if (path !== '') {
|
||||
score += Math.pow(2, level);
|
||||
}
|
||||
level++;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
export class RouterSegments {
|
||||
private path: string[];
|
||||
constructor(path: string[]) {
|
||||
|
||||
Reference in New Issue
Block a user