Skip to content

Commit cd5655a

Browse files
committed
more tests
1 parent 5a4efa2 commit cd5655a

1 file changed

Lines changed: 231 additions & 0 deletions

File tree

‎packages/router-core/tests/new-process-route-tree.test.ts‎

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,22 @@ describe('findRouteMatch', () => {
245245
const tree = makeTree(['/$a/b/c', '/a/$'])
246246
expect(findRouteMatch('/a/b/c', tree)?.route.id).toBe('/a/$')
247247
})
248+
it('case-insensitive static+wildcard beats optional+static', () => {
249+
const tree = makeTree(['/{-$a}/bar', '/foo/$'])
250+
expect(findRouteMatch('/FOO/bar', tree)?.route.id).toBe('/foo/$')
251+
})
248252
it('static+optional+static vs static+static+wildcard', () => {
249253
const tree = makeTree(['/a/{-$b}/c', '/a/b/$'])
250254
expect(findRouteMatch('/a/b/c', tree)?.route.id).toBe('/a/b/$')
251255
})
256+
it('static+optional+static vs static+static+dynamic', () => {
257+
const tree = makeTree(['/a/{-$b}/c/d', '/a/b/c/$d'])
258+
expect(findRouteMatch('/a/b/c/d', tree)?.route.id).toBe('/a/b/c/$d')
259+
})
260+
it('static+optional+static+static beats static+dynamic+static+optional', () => {
261+
const tree = makeTree(['/a/$b/c/{-$d}', '/a/{-$b}/c/d'])
262+
expect(findRouteMatch('/a/b/c/d', tree)?.route.id).toBe('/a/{-$b}/c/d')
263+
})
252264
it('static+dynamic+wildcard vs static+optional+static', () => {
253265
const tree = makeTree(['/a/$b/$', '/a/{-$b}/c'])
254266
expect(findRouteMatch('/a/b/c', tree)?.route.id).toBe('/a/{-$b}/c')
@@ -267,6 +279,10 @@ describe('findRouteMatch', () => {
267279
const tree = makeTree(['/{-$a}/b/c', '/b/$'])
268280
expect(findRouteMatch('/b/c', tree)?.route.id).toBe('/{-$a}/b/c')
269281
})
282+
it('skipped optional aligns later static priority with wildcard siblings', () => {
283+
const tree = makeTree(['/{-$a}/b/{-$c}/d', '/b/c/$'])
284+
expect(findRouteMatch('/b/c/d', tree)?.route.id).toBe('/b/c/$')
285+
})
270286
it('optional child wins over empty wildcard at the same node', () => {
271287
const tree = makeTree(['/a/{-$b}', '/a/$'])
272288
expect(findRouteMatch('/a', tree)?.route.id).toBe('/a/{-$b}')
@@ -825,6 +841,72 @@ describe('findRouteMatch', () => {
825841
expect(match?.route.id).toBe('/a/$')
826842
expect(match?.rawParams).toEqual({ '*': 'b/c', _splat: 'b/c' })
827843
})
844+
845+
it('falls back to fuzzy layout matching when wildcard param parsing fails', () => {
846+
const tree = {
847+
id: '__root__',
848+
fullPath: '/',
849+
path: '/',
850+
isRoot: true,
851+
options: {},
852+
children: [
853+
{
854+
id: '/a',
855+
fullPath: '/a',
856+
path: 'a',
857+
},
858+
{
859+
id: '/a/$',
860+
fullPath: '/a/$',
861+
path: 'a/$',
862+
options: {
863+
params: {
864+
parse: () => {
865+
throw new Error('skip')
866+
},
867+
},
868+
skipRouteOnParseError: { params: true },
869+
},
870+
},
871+
],
872+
}
873+
const { processedTree } = processRouteTree(tree)
874+
const match = findRouteMatch('/a/b/c', processedTree, true)
875+
expect(match?.route.id).toBe('/a')
876+
expect(match?.rawParams).toEqual({ '**': 'b/c' })
877+
})
878+
879+
it('falls back to exact layout matching when empty wildcard param parsing fails', () => {
880+
const tree = {
881+
id: '__root__',
882+
fullPath: '/',
883+
path: '/',
884+
isRoot: true,
885+
options: {},
886+
children: [
887+
{
888+
id: '/a',
889+
fullPath: '/a',
890+
path: 'a',
891+
},
892+
{
893+
id: '/a/$',
894+
fullPath: '/a/$',
895+
path: 'a/$',
896+
options: {
897+
params: {
898+
parse: () => {
899+
throw new Error('skip')
900+
},
901+
},
902+
skipRouteOnParseError: { params: true },
903+
},
904+
},
905+
],
906+
}
907+
const { processedTree } = processRouteTree(tree)
908+
expect(findRouteMatch('/a', processedTree)?.route.id).toBe('/a')
909+
})
828910
})
829911

830912
describe('param extraction', () => {
@@ -1003,6 +1085,49 @@ describe('findRouteMatch', () => {
10031085
expect(result?.route.id).toBe('/a/$')
10041086
expect(result?.rawParams).toEqual({ '*': 'foobar', _splat: 'foobar' })
10051087
})
1088+
it('falls back to the next matching wildcard by priority when the best wildcard parse fails', () => {
1089+
const tree = {
1090+
id: '__root__',
1091+
fullPath: '/',
1092+
path: '/',
1093+
isRoot: true,
1094+
options: {},
1095+
children: [
1096+
{
1097+
id: '/a/foo{$}bar',
1098+
fullPath: '/a/foo{$}bar',
1099+
path: 'a/foo{$}bar',
1100+
isRoot: false,
1101+
options: {
1102+
params: {
1103+
parse: () => {
1104+
throw new Error('skip')
1105+
},
1106+
},
1107+
skipRouteOnParseError: { params: true },
1108+
},
1109+
},
1110+
{
1111+
id: '/a/foo{$}',
1112+
fullPath: '/a/foo{$}',
1113+
path: 'a/foo{$}',
1114+
isRoot: false,
1115+
options: {},
1116+
},
1117+
{
1118+
id: '/a/$',
1119+
fullPath: '/a/$',
1120+
path: 'a/$',
1121+
isRoot: false,
1122+
options: {},
1123+
},
1124+
],
1125+
}
1126+
const { processedTree } = processRouteTree(tree)
1127+
const result = findRouteMatch('/a/fooxbar', processedTree)
1128+
expect(result?.route.id).toBe('/a/foo{$}')
1129+
expect(result?.rawParams).toEqual({ '*': 'xbar', _splat: 'xbar' })
1130+
})
10061131
it('does not validate a lower-priority wildcard after a better wildcard matches', () => {
10071132
let higherPriorityCalls = 0
10081133
let lowerPriorityCalls = 0
@@ -1052,6 +1177,82 @@ describe('findRouteMatch', () => {
10521177
expect(higherPriorityCalls).toBe(1)
10531178
expect(lowerPriorityCalls).toBe(0)
10541179
})
1180+
it('does not validate wildcard fallback after a static sibling matches', () => {
1181+
let calls = 0
1182+
const tree = {
1183+
id: '__root__',
1184+
fullPath: '/',
1185+
path: '/',
1186+
isRoot: true,
1187+
options: {},
1188+
children: [
1189+
{
1190+
id: '/a/b',
1191+
fullPath: '/a/b',
1192+
path: 'a/b',
1193+
isRoot: false,
1194+
options: {},
1195+
},
1196+
{
1197+
id: '/a/$',
1198+
fullPath: '/a/$',
1199+
path: 'a/$',
1200+
isRoot: false,
1201+
options: {
1202+
params: {
1203+
parse: (params: Record<string, string>) => {
1204+
calls++
1205+
return params
1206+
},
1207+
},
1208+
skipRouteOnParseError: { params: true },
1209+
},
1210+
},
1211+
],
1212+
}
1213+
const { processedTree } = processRouteTree(tree)
1214+
const result = findRouteMatch('/a/b', processedTree)
1215+
expect(result?.route.id).toBe('/a/b')
1216+
expect(calls).toBe(0)
1217+
})
1218+
it('does not validate empty wildcard fallback after an index sibling matches', () => {
1219+
let calls = 0
1220+
const tree = {
1221+
id: '__root__',
1222+
fullPath: '/',
1223+
path: '/',
1224+
isRoot: true,
1225+
options: {},
1226+
children: [
1227+
{
1228+
id: '/a/',
1229+
fullPath: '/a/',
1230+
path: 'a/',
1231+
isRoot: false,
1232+
options: {},
1233+
},
1234+
{
1235+
id: '/a/$',
1236+
fullPath: '/a/$',
1237+
path: 'a/$',
1238+
isRoot: false,
1239+
options: {
1240+
params: {
1241+
parse: (params: Record<string, string>) => {
1242+
calls++
1243+
return params
1244+
},
1245+
},
1246+
skipRouteOnParseError: { params: true },
1247+
},
1248+
},
1249+
],
1250+
}
1251+
const { processedTree } = processRouteTree(tree)
1252+
const result = findRouteMatch('/a', processedTree)
1253+
expect(result?.route.id).toBe('/a/')
1254+
expect(calls).toBe(0)
1255+
})
10551256
it('preserves parsed params from a stacked wildcard route', () => {
10561257
const tree = {
10571258
id: '__root__',
@@ -1085,6 +1286,36 @@ describe('findRouteMatch', () => {
10851286
})
10861287
})
10871288
describe('pathless routes', () => {
1289+
it('pathless nodes do not shift positional priority scores', () => {
1290+
const tree = {
1291+
id: '__root__',
1292+
fullPath: '/',
1293+
path: '/',
1294+
isRoot: true,
1295+
children: [
1296+
{
1297+
id: '/_layout',
1298+
fullPath: '/',
1299+
children: [
1300+
{
1301+
id: '/_layout/a/b',
1302+
fullPath: '/a/b',
1303+
path: 'a/b',
1304+
},
1305+
],
1306+
},
1307+
{
1308+
id: '/a/$',
1309+
fullPath: '/a/$',
1310+
path: 'a/$',
1311+
},
1312+
],
1313+
}
1314+
const { processedTree } = processRouteTree(tree)
1315+
expect(findRouteMatch('/a/b', processedTree)?.route.id).toBe(
1316+
'/_layout/a/b',
1317+
)
1318+
})
10881319
it('builds segment tree correctly', () => {
10891320
const tree = {
10901321
path: '/',

0 commit comments

Comments
 (0)