]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: encode query params
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 16 Aug 2019 17:16:57 +0000 (19:16 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Fri, 16 Aug 2019 17:16:57 +0000 (19:16 +0200)
__tests__/url-encoding.spec.js
src/history/base.ts
src/history/utils.ts

index d201b699b8eaf44e5dfc3b195c2df85191ef4d99..e93c334b40d68b2ddea5e29f9dc93129eccc5ced 100644 (file)
@@ -6,7 +6,8 @@ const { createDom, components, tick, HistoryMock } = require('./utils')
 
 /** @type {import('../src/types').RouteRecord[]} */
 const routes = [
-  { path: '/%25', name: 'home', component: components.Home },
+  { path: '/', name: 'home', component: components.Home },
+  { path: '/%25', name: 'percent', component: components.Home },
   { path: '/to-p/:p', redirect: to => `/p/${to.params.p}` },
   { path: '/p/:p', component: components.Bar },
 ]
@@ -27,7 +28,7 @@ describe('URL Encoding', () => {
       await router.doInitialNavigation()
       expect(router.currentRoute).toEqual(
         expect.objectContaining({
-          name: 'home',
+          name: 'percent',
           fullPath: '/%25',
           path: '/%25',
         })
@@ -83,5 +84,40 @@ describe('URL Encoding', () => {
         })
       )
     })
+
+    it('decodes params in query', async () => {
+      const history = createHistory('/?q=%25%E2%82%AC')
+      const router = new Router({ history, routes })
+      await router.doInitialNavigation()
+      expect(router.currentRoute).toEqual(
+        expect.objectContaining({
+          name: 'home',
+          fullPath: '/?q=' + encodeURIComponent('%€'),
+          query: {
+            q: '%€',
+          },
+          path: '/',
+        })
+      )
+    })
+
+    it('allow unencoded params in query (IE Edge)', async () => {
+      const spy = jest.spyOn(console, 'warn').mockImplementation(() => {})
+      const history = createHistory('/?q=€%notvalid')
+      const router = new Router({ history, routes })
+      await router.doInitialNavigation()
+      expect(spy).toHaveBeenCalledTimes(1)
+      spy.mockRestore()
+      expect(router.currentRoute).toEqual(
+        expect.objectContaining({
+          name: 'home',
+          fullPath: '/?q=' + encodeURIComponent('€%notvalid'),
+          query: {
+            q: '€%notvalid',
+          },
+          path: '/',
+        })
+      )
+    })
   })
 })
index f844f3228932d9575afe2f7d14fa9a510794aae2..5c878fa9d83b46088c90aa1c3cf9c6708d3b0051 100644 (file)
@@ -2,6 +2,8 @@ import * as utils from './utils'
 import { ListenerRemover } from '../types'
 
 export type HistoryQuery = Record<string, string | string[]>
+// TODO: is it reall worth allowing null to form queries like ?q&b&c
+// When parsing using URLSearchParams, `q&c=` yield an empty string for q and c
 export type RawHistoryQuery = Record<string, string | string[] | null>
 
 export interface HistoryLocation {
index 3ad842632334085335d09cccae0b50a4671a48cd..cde856d764cc4782c9193b4bf51ddf4c4932aa1f 100644 (file)
@@ -28,7 +28,7 @@ export function parseURL(location: string): HistoryLocationNormalized {
       hashPos > -1 ? hashPos : location.length
     )
 
-    query = parseQuery(searchString)
+    query = normalizeQuery(parseQuery(searchString))
   }
 
   if (hashPos > -1) {
@@ -59,7 +59,15 @@ export function parseQuery(search: string): HistoryQuery {
   if (search === '' || search === '?') return query
   const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&')
   for (let i = 0; i < searchParams.length; ++i) {
-    const [key, value] = searchParams[i].split('=')
+    let [key, value] = searchParams[i].split('=')
+    try {
+      value = decodeURIComponent(value)
+    } catch (err) {
+      // TODO: handling only URIError?
+      console.warn(
+        `[vue-router] error decoding "${value}" while parsing query. Sticking to the original value.`
+      )
+    }
     if (key in query) {
       // an extra variable for ts types
       let currentValue = query[key]
@@ -94,15 +102,26 @@ export function stringifyQuery(query: RawHistoryQuery): string {
   // TODO: util function?
   for (const key in query) {
     if (search.length > 1) search += '&'
-    // TODO: handle array
     const value = query[key]
-    if (Array.isArray(value)) {
-      search += `${key}=${value[0]}`
-      for (let i = 1; i < value.length; i++) {
-        search += `&${key}=${value[i]}`
-      }
-    } else {
-      search += `${key}=${query[key]}`
+    if (value === null) {
+      // TODO: should we just add the empty string value?
+      search += key
+      continue
+    }
+
+    let values: string[] = Array.isArray(value) ? value : [value]
+    try {
+      values = values.map(encodeURIComponent)
+    } catch (err) {
+      // TODO: handling only URIError?
+
+      console.warn(
+        `[vue-router] invalid query parameter while stringifying query: "${key}": "${values}"`
+      )
+    }
+    search += `${key}=${values[0]}`
+    for (let i = 1; i < values.length; i++) {
+      search += `&${key}=${values[i]}`
     }
   }
 
@@ -111,7 +130,13 @@ export function stringifyQuery(query: RawHistoryQuery): string {
 
 export function normalizeQuery(query: RawHistoryQuery): HistoryQuery {
   // TODO: implem
-  return query as HistoryQuery
+  const normalizedQuery: HistoryQuery = {}
+  for (const key in query) {
+    const value = query[key]
+    if (value === null) normalizedQuery[key] = ''
+    else normalizedQuery[key] = value
+  }
+  return normalizedQuery
 }
 
 /**