]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
fix(router): preserve navigation options with redirects
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 20 Apr 2020 14:26:31 +0000 (16:26 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 20 Apr 2020 14:26:31 +0000 (16:26 +0200)
__tests__/initialNavigation.spec.ts
__tests__/router.spec.ts
__tests__/utils.ts
src/router.ts
src/types/index.ts

index 985712663c9b3539f158000cbaf754adce8ea81d..3a597436227867069889224a8817aab0506023b8 100644 (file)
@@ -1,6 +1,6 @@
 import { JSDOM } from 'jsdom'
-import { createRouter, createWebHistory, Router } from '../src'
-import { createDom, components } from './utils'
+import { createRouter, createWebHistory } from '../src'
+import { createDom, components, nextNavigation } from './utils'
 import { RouteRecordRaw } from '../src/types'
 
 // override the value of isBrowser because the variable is created before JSDOM
@@ -40,21 +40,6 @@ describe('Initial Navigation', () => {
     return { history, router }
   }
 
-  function nextNavigation(router: Router) {
-    return new Promise((resolve, reject) => {
-      let removeAfter = router.afterEach((_to, _from, failure) => {
-        removeAfter()
-        removeError()
-        resolve(failure)
-      })
-      let removeError = router.onError(err => {
-        removeAfter()
-        removeError()
-        reject(err)
-      })
-    })
-  }
-
   beforeAll(() => {
     dom = createDom()
   })
index 361ac3200fdf02a3f728cd1d72f727dac40d52e9..c3c41b78cef3dd9b8e9152c852df3ce4fd558f29 100644 (file)
@@ -1,7 +1,7 @@
 import fakePromise from 'faked-promise'
 import { createRouter, createMemoryHistory, createWebHistory } from '../src'
 import { NavigationFailureType } from '../src/errors'
-import { createDom, components, tick } from './utils'
+import { createDom, components, tick, nextNavigation } from './utils'
 import {
   RouteRecordRaw,
   RouteLocationRaw,
@@ -504,6 +504,24 @@ describe('Router', () => {
       })
     })
 
+    it('keeps original replace if redirect', async () => {
+      const history = createMemoryHistory()
+      const router = createRouter({ history, routes })
+      await router.push('/search')
+
+      await expect(router.replace('/to-foo')).resolves.toEqual(undefined)
+      expect(router.currentRoute.value).toMatchObject({
+        path: '/foo',
+        redirectedFrom: expect.objectContaining({ path: '/to-foo' }),
+      })
+
+      history.go(-1)
+      await nextNavigation(router)
+      expect(router.currentRoute.value).not.toMatchObject({
+        path: '/search',
+      })
+    })
+
     it('can pass on query and hash when redirecting', async () => {
       const history = createMemoryHistory()
       const router = createRouter({ history, routes })
index 0dd5b3657aba2590a9c6befccaf30996821ffbd0..5961c9cc01b93e309d7fa2066f29dcbcbcf06278 100644 (file)
@@ -10,7 +10,7 @@ import {
   RouteRecordName,
 } from '../src/types'
 import { h, resolveComponent, ComponentOptions } from 'vue'
-import { RouterOptions, createWebHistory, createRouter } from '../src'
+import { RouterOptions, createWebHistory, createRouter, Router } from '../src'
 
 export const tick = (time?: number) =>
   new Promise(resolve => {
@@ -24,6 +24,21 @@ export async function ticks(n: number) {
   }
 }
 
+export function nextNavigation(router: Router) {
+  return new Promise((resolve, reject) => {
+    let removeAfter = router.afterEach((_to, _from, failure) => {
+      removeAfter()
+      removeError()
+      resolve(failure)
+    })
+    let removeError = router.onError(err => {
+      removeAfter()
+      removeError()
+      reject(err)
+    })
+  })
+}
+
 export interface RouteRecordViewLoose
   extends Pick<
     RouteRecordMultipleViews,
index 81d52d9039d5685cbfb98d42614a9936134dae1e..a4c6092594f7ee5f8b7432291be24dac23f6a2fe 100644 (file)
@@ -229,24 +229,29 @@ export function createRouter({
     }
   }
 
+  function locationAsObject(
+    to: RouteLocationRaw | RouteLocationNormalized
+  ): Exclude<RouteLocationRaw, string> | RouteLocationNormalized {
+    return typeof to === 'string' ? { path: to } : to
+  }
+
   function push(to: RouteLocationRaw | RouteLocation) {
-    return pushWithRedirect(to, undefined)
+    return pushWithRedirect(to)
   }
 
   function replace(to: RouteLocationRaw | RouteLocationNormalized) {
-    const location = typeof to === 'string' ? { path: to } : to
-    return push({ ...location, replace: true })
+    return push({ ...locationAsObject(to), replace: true })
   }
 
   async function pushWithRedirect(
     to: RouteLocationRaw | RouteLocation,
-    redirectedFrom: RouteLocation | undefined
+    redirectedFrom?: RouteLocation
   ): Promise<NavigationFailure | void> {
     const targetLocation: RouteLocation = (pendingLocation = resolve(to))
     const from = currentRoute.value
     const data: HistoryState | undefined = (to as any).state
-    // @ts-ignore: no need to check the string as force do not exist on a string
-    const force: boolean | undefined = to.force
+    const force: boolean | undefined = (to as any).force
+    const replace: boolean | undefined = (to as any).replace === true
 
     if (!force && isSameRouteLocation(from, targetLocation)) return
 
@@ -254,8 +259,12 @@ export function createRouter({
       targetLocation.matched[targetLocation.matched.length - 1]
     if (lastMatched && 'redirect' in lastMatched) {
       const { redirect } = lastMatched
+      // transform it into an object to pass the original RouteLocaleOptions
+      let newTargetLocation = locationAsObject(
+        typeof redirect === 'function' ? redirect(targetLocation) : redirect
+      )
       return pushWithRedirect(
-        typeof redirect === 'function' ? redirect(targetLocation) : redirect,
+        { ...newTargetLocation, state: data, force, replace },
         // keep original redirectedFrom if it exists
         redirectedFrom || targetLocation
       )
@@ -301,8 +310,7 @@ export function createRouter({
         toLocation as RouteLocationNormalizedLoaded,
         from,
         true,
-        // RouteLocationNormalized will give undefined
-        (to as RouteLocationRaw).replace === true,
+        replace,
         data
       )
 
index 4c36c02aa237cf1de8b86ffc2c3c3eb88e3b8f78..0b1a6d2b4ff46680194747e74ee7ad7687d8082f 100644 (file)
@@ -160,7 +160,10 @@ export interface _RouteRecordBase {
     | boolean
     | Record<string, any>
     | ((to: RouteLocationNormalized) => Record<string, any>)
-  // TODO: beforeEnter has no effect with redirect, move and test
+  /**
+   * Before Enter guard specific to this record. Note `beforeEnter` has no
+   * effect if the record has a `redirect` property.
+   */
   beforeEnter?:
     | NavigationGuardWithThis<undefined>
     | NavigationGuardWithThis<undefined>[]