diff --git a/__tests__/router.spec.ts b/__tests__/router.spec.ts index 84d420683..a1303e267 100644 --- a/__tests__/router.spec.ts +++ b/__tests__/router.spec.ts @@ -31,6 +31,7 @@ const routes: RouteRecordRaw[] = [ { path: '/to-foo', redirect: '/foo' }, { path: '/to-foo-named', redirect: { name: 'Foo' } }, { path: '/to-foo2', redirect: '/to-foo' }, + { path: '/to-foo-query', redirect: '/foo?a=2#b' }, { path: '/to-p/:p', redirect: { name: 'Param' } }, { path: '/p/:p', name: 'Param', component: components.Bar }, { path: '/repeat/:r+', name: 'repeat', component: components.Bar }, @@ -115,6 +116,18 @@ describe('Router', () => { expect(history.replace).toHaveBeenCalledWith('/foo', expect.anything()) }) + it('parses query and hash with router.replace', async () => { + const history = createMemoryHistory() + const { router } = await newRouter({ history }) + jest.spyOn(history, 'replace') + await router.replace('/foo?q=2#a') + expect(history.replace).toHaveBeenCalledTimes(1) + expect(history.replace).toHaveBeenCalledWith( + '/foo?q=2#a', + expect.anything() + ) + }) + it('replaces if a guard redirects', async () => { const history = createMemoryHistory() const { router } = await newRouter({ history }) @@ -537,6 +550,22 @@ describe('Router', () => { }) }) + it('handles query and hash passed in redirect string', async () => { + const history = createMemoryHistory() + const router = createRouter({ history, routes }) + await expect(router.push('/to-foo-query')).resolves.toEqual(undefined) + expect(router.currentRoute.value).toMatchObject({ + name: 'Foo', + path: '/foo', + params: {}, + query: { a: '2' }, + hash: '#b', + redirectedFrom: expect.objectContaining({ + fullPath: '/to-foo-query', + }), + }) + }) + it('keeps query and hash when redirect is a string', async () => { const history = createMemoryHistory() const router = createRouter({ history, routes }) diff --git a/src/router.ts b/src/router.ts index 5dc4279ba..d28df79c0 100644 --- a/src/router.ts +++ b/src/router.ts @@ -522,8 +522,9 @@ export function createRouter(options: RouterOptions): Router { function locationAsObject( to: RouteLocationRaw | RouteLocationNormalized ): Exclude | RouteLocationNormalized { - // FIXME: does not take into account query params - return typeof to === 'string' ? { path: to } : assign({}, to) + return typeof to === 'string' + ? parseURL(parseQuery, to, currentRoute.value.path) + : assign({}, to) } function checkCanceledNavigation( @@ -553,10 +554,16 @@ export function createRouter(options: RouterOptions): Router { const lastMatched = to.matched[to.matched.length - 1] if (lastMatched && lastMatched.redirect) { const { redirect } = lastMatched - // transform it into an object to pass the original RouteLocaleOptions - let newTargetLocation = locationAsObject( + let newTargetLocation = typeof redirect === 'function' ? redirect(to) : redirect - ) + + if (typeof newTargetLocation === 'string') { + newTargetLocation = + newTargetLocation.indexOf('?') > -1 || + newTargetLocation.indexOf('#') > -1 + ? (newTargetLocation = locationAsObject(newTargetLocation)) + : { path: newTargetLocation } + } if ( __DEV__ && @@ -587,9 +594,7 @@ export function createRouter(options: RouterOptions): Router { } function pushWithRedirect( - // TODO: should only be RouteLocation? to: RouteLocationRaw | RouteLocation, - // TODO: add replace here redirectedFrom?: RouteLocation ): Promise { const targetLocation: RouteLocation = (pendingLocation = resolve(to)) @@ -603,7 +608,11 @@ export function createRouter(options: RouterOptions): Router { if (shouldRedirect) return pushWithRedirect( - assign(shouldRedirect, { state: data, force, replace }), + assign(locationAsObject(shouldRedirect), { + state: data, + force, + replace, + }), // keep original redirectedFrom if it exists redirectedFrom || targetLocation )