From d39d0fd15a821607b5ddf9a9e32df0f7079f3b7e Mon Sep 17 00:00:00 2001 From: kshramt Date: Sat, 31 Aug 2024 22:33:20 +0900 Subject: [PATCH] fix(react-router): decode search keys (#2227) * fix: Decode search keys * chore: Add a few more tests for search param key decoding * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- packages/react-router/src/qss.ts | 2 ++ packages/react-router/tests/navigate.test.tsx | 27 +++++++++++++++++++ packages/react-router/tests/qss.test.ts | 24 +++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/packages/react-router/src/qss.ts b/packages/react-router/src/qss.ts index b5ec8e9e4d..b08d5878f1 100644 --- a/packages/react-router/src/qss.ts +++ b/packages/react-router/src/qss.ts @@ -68,6 +68,7 @@ export function decode(str: any, pfx?: string) { const equalIndex = tmp.indexOf('=') if (equalIndex !== -1) { k = tmp.slice(0, equalIndex) + k = decodeURIComponent(k) const value = tmp.slice(equalIndex + 1) if (out[k] !== void 0) { // @ts-expect-error @@ -77,6 +78,7 @@ export function decode(str: any, pfx?: string) { } } else { k = tmp + k = decodeURIComponent(k) out[k] = '' } } diff --git a/packages/react-router/tests/navigate.test.tsx b/packages/react-router/tests/navigate.test.tsx index 61e34f55cd..cf91b66122 100644 --- a/packages/react-router/tests/navigate.test.tsx +++ b/packages/react-router/tests/navigate.test.tsx @@ -68,6 +68,15 @@ function createTestRouter(initialHistory?: RouterHistory) { getParentRoute: () => gLayoutRoute, path: '$username', }) + const searchRoute = createRoute({ + getParentRoute: () => rootRoute, + path: 'search', + validateSearch: (search: Record) => { + return { + ['foo=bar']: Number(search['foo=bar'] ?? 1), + } + }, + }) const projectTree = projectRoute.addChildren([ projectIdRoute.addChildren([ @@ -460,4 +469,22 @@ describe('router.navigate navigation using layout routes resolves correctly', as expect(router.state.location.pathname).toBe('/g/tkdodo') }) + + it('should handle search params with special characters', async () => { + const { router } = createTestRouter( + createMemoryHistory({ initialEntries: ['/search?foo%3Dbar=2'] }), + ) + + await router.load() + + expect(router.state.location.pathname).toBe('/search') + expect(router.state.location.search).toStrictEqual({ 'foo=bar': 2 }) + + await router.navigate({ + search: { 'foo=bar': 3 }, + }) + await router.invalidate() + + expect(router.state.location.search).toStrictEqual({ 'foo=bar': 3 }) + }) }) diff --git a/packages/react-router/tests/qss.test.ts b/packages/react-router/tests/qss.test.ts index b7aebec203..7f1bbca239 100644 --- a/packages/react-router/tests/qss.test.ts +++ b/packages/react-router/tests/qss.test.ts @@ -32,6 +32,12 @@ describe('encode function', () => { const queryString = encode(obj) expect(queryString).toEqual('token=foo%3F&key=value%3D') }) + + it('should handle encoding a top-level key with a special character', () => { + const obj = { 'foo=bar': 1 } + const queryString = encode(obj) + expect(queryString).toEqual('foo%3Dbar=1') + }) }) describe('decode function', () => { @@ -64,4 +70,22 @@ describe('decode function', () => { const decodedObj = decode(queryString) expect(decodedObj).toEqual({ token: 'foo?', key: 'value=' }) }) + + it('should handle decoding a top-level key with a special character', () => { + const queryString = 'foo%3Dbar=1' + const decodedObj = decode(queryString) + expect(decodedObj).toEqual({ 'foo=bar': 1 }) + }) + + it('should handle decoding a top-level key with a special character and without a value', () => { + const queryString = 'foo%3Dbar=' + const decodedObj = decode(queryString) + expect(decodedObj).toEqual({ 'foo=bar': '' }) + }) + + it('should handle decoding a value-less top-level key with a special character', () => { + const queryString = 'foo%3Dbar' + const decodedObj = decode(queryString) + expect(decodedObj).toEqual({ 'foo=bar': '' }) + }) })