From: Eduardo San Martin Morote Date: Mon, 20 Apr 2020 14:26:31 +0000 (+0200) Subject: fix(router): preserve navigation options with redirects X-Git-Tag: v4.0.0-alpha.8~69 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9732758d076eef252f2940ffa44e44fa94e794a0;p=thirdparty%2Fvuejs%2Frouter.git fix(router): preserve navigation options with redirects --- diff --git a/__tests__/initialNavigation.spec.ts b/__tests__/initialNavigation.spec.ts index 98571266..3a597436 100644 --- a/__tests__/initialNavigation.spec.ts +++ b/__tests__/initialNavigation.spec.ts @@ -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() }) diff --git a/__tests__/router.spec.ts b/__tests__/router.spec.ts index 361ac320..c3c41b78 100644 --- a/__tests__/router.spec.ts +++ b/__tests__/router.spec.ts @@ -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 }) diff --git a/__tests__/utils.ts b/__tests__/utils.ts index 0dd5b365..5961c9cc 100644 --- a/__tests__/utils.ts +++ b/__tests__/utils.ts @@ -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, diff --git a/src/router.ts b/src/router.ts index 81d52d90..a4c60925 100644 --- a/src/router.ts +++ b/src/router.ts @@ -229,24 +229,29 @@ export function createRouter({ } } + function locationAsObject( + to: RouteLocationRaw | RouteLocationNormalized + ): Exclude | 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 { 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 ) diff --git a/src/types/index.ts b/src/types/index.ts index 4c36c02a..0b1a6d2b 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -160,7 +160,10 @@ export interface _RouteRecordBase { | boolean | Record | ((to: RouteLocationNormalized) => Record) - // 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 | NavigationGuardWithThis[]