fix(router): ambiguous routes

This commit is contained in:
Manu Mtz.-Almeida
2018-03-13 23:33:31 +01:00
parent 3b5f758318
commit b4f46ee3d2
4 changed files with 83 additions and 33 deletions

View File

@@ -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;
}

View File

@@ -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)));
}

View File

@@ -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 },

View File

@@ -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[]) {