|
1 | 1 | import { describe, expect, it } from 'vitest' |
2 | | -import { findRouteMatch, processRouteTree } from '../src/new-process-route-tree' |
| 2 | +import { |
| 3 | + findFlatMatch, |
| 4 | + findRouteMatch, |
| 5 | + processRouteMasks, |
| 6 | + processRouteTree, |
| 7 | +} from '../src/new-process-route-tree' |
3 | 8 | import type { AnyRoute, RouteMask } from '../src' |
4 | 9 |
|
5 | 10 | function makeTree(routes: Array<string>) { |
@@ -191,10 +196,92 @@ describe('findRouteMatch', () => { |
191 | 196 | expect(findRouteMatch('/a/b/c/d/e', tree)?.route.id).toBe('/a/b/c/$') |
192 | 197 | expect(findRouteMatch('/a/d/e', tree)?.route.id).toBe('/a/$') |
193 | 198 | }) |
| 199 | + |
| 200 | + it('matching a single dynamic param is favored over matching any number of optional params', () => { |
| 201 | + const tree = makeTree(['/$a/z', '/{-$a}/{-$b}/{-$c}/{-$d}/{-$e}/z']) |
| 202 | + expect(findRouteMatch('/a/z', tree)?.route.id).toBe('/$a/z') |
| 203 | + expect(findRouteMatch('/a/b/z', tree)?.route.id).toBe( |
| 204 | + '/{-$a}/{-$b}/{-$c}/{-$d}/{-$e}/z', |
| 205 | + ) |
| 206 | + }) |
| 207 | + it('matching a single dynamic param is favored over matching any number of optional params (2)', () => { |
| 208 | + const tree = makeTree(['/{-$a}/$b', '/{-$a}']) |
| 209 | + expect(findRouteMatch('/a', tree)?.route.id).toBe('/{-$a}/$b') |
| 210 | + expect(findRouteMatch('/a/b', tree)?.route.id).toBe('/{-$a}/$b') |
| 211 | + }) |
| 212 | + describe('optional param can be a route on its own, but matching a static or dynamic is preferred', () => { |
| 213 | + it('on its own', () => { |
| 214 | + const tree = makeTree(['/a/{-$b}/']) |
| 215 | + expect(findRouteMatch('/a/b', tree)?.route.id).toBe('/a/{-$b}/') |
| 216 | + }) |
| 217 | + it('vs dynamic sibling', () => { |
| 218 | + const tree = makeTree(['/a/{-$b}/', '/a/$b']) |
| 219 | + expect(findRouteMatch('/a/b', tree)?.route.id).toBe('/a/$b') |
| 220 | + }) |
| 221 | + it('vs dynamic child', () => { |
| 222 | + const tree = makeTree(['/a/{-$b}/', '/a/{-$b}/$c']) |
| 223 | + expect(findRouteMatch('/a/b', tree)?.route.id).toBe('/a/{-$b}/$c') |
| 224 | + }) |
| 225 | + }) |
194 | 226 | }) |
195 | 227 | }) |
196 | 228 |
|
197 | | - describe.todo('trailing slashes', () => {}) |
| 229 | + describe('trailing slashes (index routes)', () => { |
| 230 | + describe('static routes', () => { |
| 231 | + it('an index route can be matched by a path with a trailing slash', () => { |
| 232 | + const tree = makeTree(['/a/']) |
| 233 | + expect(findRouteMatch('/a/', tree)?.route.id).toBe('/a/') |
| 234 | + }) |
| 235 | + it('an index route can be matched by a path without a trailing slash', () => { |
| 236 | + const tree = makeTree(['/a/']) |
| 237 | + expect(findRouteMatch('/a', tree)?.route.id).toBe('/a/') |
| 238 | + }) |
| 239 | + it('a non-index route CANNOT be matched by a path with a trailing slash', () => { |
| 240 | + const tree = makeTree(['/a']) |
| 241 | + expect(findRouteMatch('/a/', tree)).toBeNull() |
| 242 | + }) |
| 243 | + it('a non-index route can be matched by a path without a trailing slash', () => { |
| 244 | + const tree = makeTree(['/a']) |
| 245 | + expect(findRouteMatch('/a', tree)?.route.id).toBe('/a') |
| 246 | + }) |
| 247 | + }) |
| 248 | + describe('dynamic routes', () => { |
| 249 | + it('an index route can be matched by a path with a trailing slash', () => { |
| 250 | + const tree = makeTree(['/$a/']) |
| 251 | + expect(findRouteMatch('/a/', tree)?.route.id).toBe('/$a/') |
| 252 | + }) |
| 253 | + it('an index route can be matched by a path without a trailing slash', () => { |
| 254 | + const tree = makeTree(['/$a/']) |
| 255 | + expect(findRouteMatch('/a', tree)?.route.id).toBe('/$a/') |
| 256 | + }) |
| 257 | + it('a non-index route CANNOT be matched by a path with a trailing slash', () => { |
| 258 | + const tree = makeTree(['/$a']) |
| 259 | + expect(findRouteMatch('/a/', tree)).toBeNull() |
| 260 | + }) |
| 261 | + it('a non-index route can be matched by a path without a trailing slash', () => { |
| 262 | + const tree = makeTree(['/$a']) |
| 263 | + expect(findRouteMatch('/a', tree)?.route.id).toBe('/$a') |
| 264 | + }) |
| 265 | + }) |
| 266 | + describe('optional routes', () => { |
| 267 | + it('an index route can be matched by a path with a trailing slash', () => { |
| 268 | + const tree = makeTree(['/{-$a}/']) |
| 269 | + expect(findRouteMatch('/a/', tree)?.route.id).toBe('/{-$a}/') |
| 270 | + }) |
| 271 | + it('an index route can be matched by a path without a trailing slash', () => { |
| 272 | + const tree = makeTree(['/{-$a}/']) |
| 273 | + expect(findRouteMatch('/a', tree)?.route.id).toBe('/{-$a}/') |
| 274 | + }) |
| 275 | + it('a non-index route CANNOT be matched by a path with a trailing slash', () => { |
| 276 | + const tree = makeTree(['/{-$a}']) |
| 277 | + expect(findRouteMatch('/a/', tree)).toBeNull() |
| 278 | + }) |
| 279 | + it('a non-index route can be matched by a path without a trailing slash', () => { |
| 280 | + const tree = makeTree(['/{-$a}']) |
| 281 | + expect(findRouteMatch('/a', tree)?.route.id).toBe('/{-$a}') |
| 282 | + }) |
| 283 | + }) |
| 284 | + }) |
198 | 285 |
|
199 | 286 | describe('case sensitivity competition', () => { |
200 | 287 | it('a case sensitive segment early on should not prevent a case insensitive match', () => { |
@@ -702,16 +789,45 @@ describe('findRouteMatch', () => { |
702 | 789 | }) |
703 | 790 | }) |
704 | 791 |
|
705 | | -describe.todo('processRouteMasks', () => { |
706 | | - it('processes a route masks list', () => { |
707 | | - const routeTree = {} as AnyRoute |
| 792 | +describe('processRouteMasks', { sequential: true }, () => { |
| 793 | + const routeTree = { |
| 794 | + id: '__root__', |
| 795 | + isRoot: true, |
| 796 | + fullPath: '/', |
| 797 | + } as AnyRoute |
| 798 | + const { processedTree } = processRouteTree(routeTree) |
| 799 | + it('processes a route masks list into a segment tree', () => { |
708 | 800 | const routeMasks: Array<RouteMask<AnyRoute>> = [ |
709 | 801 | { from: '/a/b/c', routeTree }, |
710 | 802 | { from: '/a/b/d', routeTree }, |
711 | 803 | { from: '/a/$param/d', routeTree }, |
712 | 804 | { from: '/a/{-$optional}/d', routeTree }, |
713 | 805 | { from: '/a/b/{$}.txt', routeTree }, |
714 | 806 | ] |
715 | | - // expect(processRouteMasks(routeMasks)).toMatchInlineSnapshot() |
| 807 | + processRouteMasks(routeMasks, processedTree) |
| 808 | + const aBranch = processedTree.masksTree?.staticInsensitive?.get('a') |
| 809 | + expect(aBranch).toBeDefined() |
| 810 | + expect(aBranch?.staticInsensitive?.get('b')).toBeDefined() |
| 811 | + expect(aBranch?.dynamic).toHaveLength(1) |
| 812 | + expect(aBranch?.optional).toHaveLength(1) |
| 813 | + }) |
| 814 | + it('can match static routes masks w/ `findFlatMatch`', () => { |
| 815 | + const res = findFlatMatch('/a/b/c', processedTree) |
| 816 | + expect(res?.route.from).toBe('/a/b/c') |
| 817 | + }) |
| 818 | + it('can match dynamic route masks w/ `findFlatMatch`', () => { |
| 819 | + const res = findFlatMatch('/a/123/d', processedTree) |
| 820 | + expect(res?.route.from).toBe('/a/$param/d') |
| 821 | + expect(res?.params).toEqual({ param: '123' }) |
| 822 | + }) |
| 823 | + it('can match optional route masks w/ `findFlatMatch`', () => { |
| 824 | + const res = findFlatMatch('/a/d', processedTree) |
| 825 | + expect(res?.route.from).toBe('/a/{-$optional}/d') |
| 826 | + expect(res?.params).toEqual({}) |
| 827 | + }) |
| 828 | + it('can match prefix/suffix wildcard route masks w/ `findFlatMatch`', () => { |
| 829 | + const res = findFlatMatch('/a/b/file/path.txt', processedTree) |
| 830 | + expect(res?.route.from).toBe('/a/b/{$}.txt') |
| 831 | + expect(res?.params).toEqual({ '*': 'file/path', _splat: 'file/path' }) |
716 | 832 | }) |
717 | 833 | }) |
0 commit comments