| author | |
| committer | |
| log | df26ef99859ac757409f5332b185a89cc7f40267 |
| tree | c110820d985ecc0b9c643c31cff16cb49f816fab |
| parent | 41d86c1b1c273ed662d59c90aa9685476bac962c |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>3 files changed, 44 insertions(+), 2 deletions(-)
apps/web/src/components/pullRequest/pullRequestListPreferences.ts+1-1| ... | ... | @@ -25,7 +25,7 @@ export interface PullRequestListPreferences { |
| 25 | 25 | readonly environmentId?: EnvironmentId; |
| 26 | 26 | readonly projectId?: ProjectId; |
| 27 | 27 | readonly host?: string; |
| 28 | readonly q?: string; | |
| 28 | readonly q?: string | undefined; | |
| 29 | 29 | readonly draft?: "only" | "hide"; |
| 30 | 30 | readonly review?: NonNullable<PullRequestListFilters["review"]>; |
| 31 | 31 | readonly checks?: NonNullable<PullRequestListFilters["checks"]>; |
apps/web/src/components/pullRequest/pullRequestsRouteSearch.test.ts created+40| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | import { | |
| 2 | createMemoryHistory, | |
| 3 | createRootRoute, | |
| 4 | createRoute, | |
| 5 | createRouter, | |
| 6 | } from "@tanstack/react-router"; | |
| 7 | import { describe, expect, it } from "vite-plus/test"; | |
| 8 | ||
| 9 | import { Route as PullRequestsRoute } from "../../routes/_chat.pull-requests"; | |
| 10 | ||
| 11 | /** Matches a URL in a router shaped like the app's: a layout without its own search rules. */ | |
| 12 | function matchedSearch(url: string) { | |
| 13 | const root = createRootRoute(); | |
| 14 | const chat = createRoute({ getParentRoute: () => root, id: "_chat" }); | |
| 15 | const pullRequests = createRoute({ | |
| 16 | getParentRoute: () => chat, | |
| 17 | path: "pull-requests", | |
| 18 | validateSearch: PullRequestsRoute.options.validateSearch, | |
| 19 | }); | |
| 20 | const router = createRouter({ | |
| 21 | routeTree: root.addChildren([chat.addChildren([pullRequests])]), | |
| 22 | history: createMemoryHistory({ initialEntries: [url] }), | |
| 23 | }); | |
| 24 | // The search a component reads through `useSearch` is the matched route's, not the URL's. | |
| 25 | return router.matchRoutes(router.latestLocation).at(-1)?.search as | |
| 26 | | Record<string, unknown> | |
| 27 | | undefined; | |
| 28 | } | |
| 29 | ||
| 30 | describe("pull requests route search", () => { | |
| 31 | it("drops a q the URL parser turned into a number", () => { | |
| 32 | expect(matchedSearch("/pull-requests?q=4089")?.q).toBeUndefined(); | |
| 33 | expect(matchedSearch("/pull-requests?q=true")?.q).toBeUndefined(); | |
| 34 | }); | |
| 35 | ||
| 36 | it("keeps a q that is text, including quoted digits", () => { | |
| 37 | expect(matchedSearch("/pull-requests?q=fix")?.q).toBe("fix"); | |
| 38 | expect(matchedSearch("/pull-requests?q=%224089%22")?.q).toBe("4089"); | |
| 39 | }); | |
| 40 | }); |
apps/web/src/routes/_chat.pull-requests.tsx+3-1| ... | ... | @@ -332,7 +332,9 @@ export const Route = createFileRoute("/_chat/pull-requests")({ |
| 332 | 332 | ...(typeof raw.selectedEnvironmentId === "string" && raw.selectedEnvironmentId |
| 333 | 333 | ? { selectedEnvironmentId: raw.selectedEnvironmentId as EnvironmentId } |
| 334 | 334 | : {}), |
| 335 | ...(typeof raw.q === "string" && raw.q ? { q: raw.q.slice(0, 200) } : {}), | |
| 335 | // Always an own key: the router lays this result over the parent route's unvalidated | |
| 336 | // search, so leaving out a `q` the URL parser made a number would let the number through. | |
| 337 | q: typeof raw.q === "string" && raw.q ? raw.q.slice(0, 200) : undefined, | |
| 336 | 338 | ...(raw.draft === "only" || raw.draft === "hide" ? { draft: raw.draft } : {}), |
| 337 | 339 | ...(raw.review === "approved" || |
| 338 | 340 | raw.review === "changes-requested" || |