Skip to content

Commit cac7a3b

Browse files
committed
improve tests
1 parent c35ac00 commit cac7a3b

File tree

1 file changed

+132
-49
lines changed

1 file changed

+132
-49
lines changed

packages/router-core/tests/skip-route-on-parse-error.test.ts

Lines changed: 132 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, expect, it } from 'vitest'
1+
import { describe, expect, it, vi } from 'vitest'
22
import { findRouteMatch, processRouteTree } from '../src/new-process-route-tree'
33

44
describe('skipRouteOnParseError', () => {
@@ -180,6 +180,46 @@ describe('skipRouteOnParseError', () => {
180180
const result = findRouteMatch('/settings', processedTree)
181181
expect(result?.route.id).toBe('/settings')
182182
})
183+
184+
it('deep validated route can still fallback to sibling', () => {
185+
const tree = {
186+
id: '__root__',
187+
isRoot: true,
188+
fullPath: '/',
189+
children: [
190+
{
191+
id: '/$a/$b/$c',
192+
fullPath: '/$a/$b/$c',
193+
path: '$a/$b/$c',
194+
options: {
195+
params: {
196+
parse: (params: Record<string, string>) => {
197+
// if (params.a !== 'one') throw new Error('Invalid a')
198+
// if (params.b !== 'two') throw new Error('Invalid b')
199+
if (params.c !== 'three') throw new Error('Invalid c')
200+
return params
201+
},
202+
},
203+
skipRouteOnParseError: { params: true },
204+
},
205+
},
206+
{
207+
id: '/$x/$y/$z',
208+
fullPath: '/$x/$y/$z',
209+
path: '$x/$y/$z',
210+
},
211+
],
212+
}
213+
const { processedTree } = processRouteTree(tree)
214+
{
215+
const result = findRouteMatch('/one/two/three', processedTree)
216+
expect(result?.route.id).toBe('/$a/$b/$c')
217+
}
218+
{
219+
const result = findRouteMatch('/one/two/wrong', processedTree)
220+
expect(result?.route.id).toBe('/$x/$y/$z')
221+
}
222+
})
183223
})
184224

185225
describe('regex-like validation patterns', () => {
@@ -444,6 +484,10 @@ describe('skipRouteOnParseError', () => {
444484
const helloResult = findRouteMatch('/abc/hello', processedTree)
445485
expect(helloResult?.route.id).toBe('/$foo/hello')
446486
expect(helloResult?.rawParams).toEqual({ foo: 'abc' })
487+
488+
// non-numeric foo should NOT match the children of the validated layout
489+
const barResult = findRouteMatch('/abc/bar', processedTree)
490+
expect(barResult).toBeNull()
447491
})
448492
})
449493

@@ -496,7 +540,7 @@ describe('skipRouteOnParseError', () => {
496540

497541
// invalid language should NOT match the validated optional route
498542
// and since there's no dynamic fallback, it should return null
499-
const invalidResult = findRouteMatch('/about/home', processedTree)
543+
const invalidResult = findRouteMatch('/it/home', processedTree)
500544
expect(invalidResult).toBeNull()
501545
})
502546

@@ -660,84 +704,123 @@ describe('skipRouteOnParseError', () => {
660704
const slugResult = findRouteMatch('/hello-world', processedTree)
661705
expect(slugResult?.route.id).toBe('/$slug')
662706
})
663-
})
664-
665-
describe('params.parse without skipRouteOnParseError', () => {
666-
it('params.parse is NOT called during matching when skipRouteOnParseError is false', () => {
667-
let parseCalled = false
668-
const tree = {
707+
it('priority option can be used to influence order', () => {
708+
const alphabetical = {
669709
id: '__root__',
670710
isRoot: true,
671711
fullPath: '/',
672712
path: '/',
673713
children: [
674714
{
675-
id: '/$id',
676-
fullPath: '/$id',
677-
path: '$id',
715+
id: '/$a',
716+
fullPath: '/$a',
717+
path: '$a',
678718
options: {
679719
params: {
680-
parse: (params: Record<string, string>) => {
681-
parseCalled = true
682-
return { id: parseInt(params.id!, 10) }
683-
},
720+
parse: (params: Record<string, string>) => params,
721+
},
722+
skipRouteOnParseError: {
723+
params: true,
724+
priority: 1, // higher priority than /$z
725+
},
726+
},
727+
},
728+
{
729+
id: '/$z',
730+
fullPath: '/$z',
731+
path: '$z',
732+
options: {
733+
params: {
734+
parse: (params: Record<string, string>) => params,
735+
},
736+
skipRouteOnParseError: {
737+
params: true,
738+
priority: -1, // lower priority than /$a
684739
},
685-
// skipRouteOnParseError is NOT set
686740
},
687741
},
688742
],
689743
}
690-
const { processedTree } = processRouteTree(tree)
691-
const result = findRouteMatch('/123', processedTree)
692-
expect(result?.route.id).toBe('/$id')
693-
// parse should NOT be called during matching
694-
expect(parseCalled).toBe(false)
695-
// params should be raw strings
696-
expect(result?.rawParams).toEqual({ id: '123' })
697-
})
698-
})
699-
700-
describe('edge cases', () => {
701-
it('empty param value still goes through validation', () => {
702-
const tree = {
744+
{
745+
const { processedTree } = processRouteTree(alphabetical)
746+
const result = findRouteMatch('/123', processedTree)
747+
expect(result?.route.id).toBe('/$a')
748+
}
749+
const reverse = {
703750
id: '__root__',
704751
isRoot: true,
705752
fullPath: '/',
706753
path: '/',
707754
children: [
708755
{
709-
id: '/prefix{$id}suffix',
710-
fullPath: '/prefix{$id}suffix',
711-
path: 'prefix{$id}suffix',
756+
id: '/$a',
757+
fullPath: '/$a',
758+
path: '$a',
712759
options: {
713760
params: {
714-
parse: (params: Record<string, string>) => {
715-
if (params.id === '') throw new Error('Empty not allowed')
716-
return params
717-
},
761+
parse: (params: Record<string, string>) => params,
762+
},
763+
skipRouteOnParseError: {
764+
params: true,
765+
priority: -1, // lower priority than /$z
718766
},
719-
skipRouteOnParseError: { params: true },
720767
},
721768
},
722769
{
723-
id: '/prefixsuffix',
724-
fullPath: '/prefixsuffix',
725-
path: 'prefixsuffix',
726-
options: {},
770+
id: '/$z',
771+
fullPath: '/$z',
772+
path: '$z',
773+
options: {
774+
params: {
775+
parse: (params: Record<string, string>) => params,
776+
},
777+
skipRouteOnParseError: {
778+
params: true,
779+
priority: 1, // higher priority than /$a
780+
},
781+
},
727782
},
728783
],
729784
}
730-
const { processedTree } = processRouteTree(tree)
731-
732-
// with value should match validated route
733-
const withValue = findRouteMatch('/prefixFOOsuffix', processedTree)
734-
expect(withValue?.route.id).toBe('/prefix{$id}suffix')
785+
{
786+
const { processedTree } = processRouteTree(reverse)
787+
const result = findRouteMatch('/123', processedTree)
788+
expect(result?.route.id).toBe('/$z')
789+
}
790+
})
791+
})
735792

736-
// empty value should fall through to static route
737-
const empty = findRouteMatch('/prefixsuffix', processedTree)
738-
expect(empty?.route.id).toBe('/prefixsuffix')
793+
describe('params.parse without skipRouteOnParseError', () => {
794+
it('params.parse is NOT called during matching when skipRouteOnParseError is false', () => {
795+
const parse = vi.fn()
796+
const tree = {
797+
id: '__root__',
798+
isRoot: true,
799+
fullPath: '/',
800+
path: '/',
801+
children: [
802+
{
803+
id: '/$id',
804+
fullPath: '/$id',
805+
path: '$id',
806+
options: {
807+
params: { parse },
808+
// skipRouteOnParseError is NOT set
809+
},
810+
},
811+
],
812+
}
813+
const { processedTree } = processRouteTree(tree)
814+
const result = findRouteMatch('/123', processedTree)
815+
expect(result?.route.id).toBe('/$id')
816+
// parse should NOT be called during matching
817+
expect(parse).not.toHaveBeenCalled()
818+
// params should be raw strings
819+
expect(result?.rawParams).toEqual({ id: '123' })
739820
})
821+
})
740822

823+
describe('edge cases', () => {
741824
it('validation error type does not matter', () => {
742825
const tree = {
743826
id: '__root__',

0 commit comments

Comments
 (0)