also looking at this
fix(serve): dev no-store caching + nested dynamic route regex
#1731
2 files
+120
-35
| @@ -0,0 +1,63 @@ | ||
| 1 | import { describe, expect, it } from 'bun:test' | |
| 2 | import { buildDynamicRouteRegexes } from '../src/serve' | |
| 3 | ||
| 4 | /** | |
| 5 | * Dynamic-route regex construction (stacksjs/stx#1927). | |
| 6 | * | |
| 7 | * Nested dynamic routes (e.g. `judges/[id]/profile/index.stx`) used to | |
| 8 | * 404 on hard reload because the route regex kept the trailing | |
| 9 | * `/index`, so `^judges/([^/]+)/profile/index$` never matched the URL | |
| 10 | * `judges/35/profile`. These tests pin the `/index`-stripped variant. | |
| 11 | */ | |
| 12 | ||
| 13 | function matchesAny(fileRouteBase: string, url: string): boolean { | |
| 14 | return buildDynamicRouteRegexes(fileRouteBase).some(re => re.test(url)) | |
| 15 | } | |
| 16 | ||
| 17 | describe('buildDynamicRouteRegexes — single-segment dynamic routes', () => { | |
| 18 | it('matches /judges/35 from judges/[id]', () => { | |
| 19 | expect(matchesAny('judges/[id]', 'judges/35')).toBe(true) | |
| 20 | }) | |
| 21 | ||
| 22 | it('matches /judges/35 from judges/[id]/index', () => { | |
| 23 | expect(matchesAny('judges/[id]/index', 'judges/35')).toBe(true) | |
| 24 | }) | |
| 25 | }) | |
| 26 | ||
| 27 | describe('buildDynamicRouteRegexes — nested dynamic routes (the bug)', () => { | |
| 28 | it('matches /judges/35/profile from judges/[id]/profile/index', () => { | |
| 29 | expect(matchesAny('judges/[id]/profile/index', 'judges/35/profile')).toBe(true) | |
| 30 | }) | |
| 31 | ||
| 32 | it('matches /judges/35/reviews from judges/[id]/reviews/index', () => { | |
| 33 | expect(matchesAny('judges/[id]/reviews/index', 'judges/35/reviews')).toBe(true) | |
| 34 | }) | |
| 35 | ||
| 36 | it('matches /court-houses/1/bench from court-houses/[id]/bench/index', () => { | |
| 37 | expect(matchesAny('court-houses/[id]/bench/index', 'court-houses/1/bench')).toBe(true) | |
| 38 | }) | |
| 39 | ||
| 40 | it('still matches the index-included URL for back-compat', () => { | |
| 41 | expect(matchesAny('judges/[id]/profile/index', 'judges/35/profile/index')).toBe(true) | |
| 42 | }) | |
| 43 | ||
| 44 | it('does NOT match an unrelated path', () => { | |
| 45 | expect(matchesAny('judges/[id]/profile/index', 'judges/35/cases')).toBe(false) | |
| 46 | }) | |
| 47 | ||
| 48 | it('captures the dynamic segment, not the static suffix', () => { | |
| 49 | const [, noIndexRe] = buildDynamicRouteRegexes('judges/[id]/profile/index') | |
| 50 | const m = 'judges/35/profile'.match(noIndexRe) | |
| 51 | expect(m?.[1]).toBe('35') | |
| 52 | }) | |
| 53 | }) | |
| 54 | ||
| 55 | describe('buildDynamicRouteRegexes — pages/ prefix', () => { | |
| 56 | it('matches the pages/-stripped URL', () => { | |
| 57 | expect(matchesAny('pages/judges/[id]/profile/index', 'judges/35/profile')).toBe(true) | |
| 58 | }) | |
| 59 | ||
| 60 | it('matches the full pages/ path too', () => { | |
| 61 | expect(matchesAny('pages/data/[model]', 'pages/data/users')).toBe(true) | |
| 62 | }) | |
| 63 | }) | |