From 05863948ee86e0f1c9c9ec31c02ad7af17923743 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Mon, 20 Apr 2020 15:03:21 +0200 Subject: [PATCH] fix(html5): correctly preserve current history.state Fix #180 --- __tests__/initialNavigation.spec.ts | 91 +++++++++++++++++++++++++++++ __tests__/utils.ts | 2 + src/history/html5.ts | 2 +- 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 __tests__/initialNavigation.spec.ts diff --git a/__tests__/initialNavigation.spec.ts b/__tests__/initialNavigation.spec.ts new file mode 100644 index 00000000..98571266 --- /dev/null +++ b/__tests__/initialNavigation.spec.ts @@ -0,0 +1,91 @@ +import { JSDOM } from 'jsdom' +import { createRouter, createWebHistory, Router } from '../src' +import { createDom, components } from './utils' +import { RouteRecordRaw } from '../src/types' + +// override the value of isBrowser because the variable is created before JSDOM +// is created +jest.mock('../src/utils/env', () => ({ + isBrowser: true, +})) + +// generic component because we are not displaying anything so it doesn't matter +const component = components.Home + +const routes: RouteRecordRaw[] = [ + { path: '/home', redirect: '/' }, + { path: '/', component }, + { + path: '/home-before', + component, + beforeEnter: (to, from, next) => { + next('/') + }, + }, + { path: '/bar', component }, + { path: '/foo', component, name: 'Foo' }, + { path: '/to-foo', redirect: '/foo' }, +] + +describe('Initial Navigation', () => { + let dom: JSDOM + function newRouter( + url: string, + options: Partial[0]> = {} + ) { + dom.reconfigure({ url: 'https://example.com' + url }) + const history = options.history || createWebHistory() + const router = createRouter({ history, routes, ...options }) + + 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() + }) + + afterAll(() => { + dom.window.close() + }) + + it('handles initial navigation with redirect', async () => { + const { history, router } = newRouter('/home') + expect(history.location.fullPath).toBe('/home') + // this is done automatically on mount but there is no mount here + await router.push(history.location.fullPath) + expect(router.currentRoute.value).toMatchObject({ path: '/' }) + await router.push('/foo') + expect(router.currentRoute.value).toMatchObject({ path: '/foo' }) + history.go(-1) + await nextNavigation(router) + expect(router.currentRoute.value).toMatchObject({ path: '/' }) + }) + + it('handles initial navigation with beforEnter', async () => { + const { history, router } = newRouter('/home-before') + expect(history.location.fullPath).toBe('/home-before') + // this is done automatically on mount but there is no mount here + await router.push(history.location.fullPath) + expect(router.currentRoute.value).toMatchObject({ path: '/' }) + await router.push('/foo') + expect(router.currentRoute.value).toMatchObject({ path: '/foo' }) + history.go(-1) + await nextNavigation(router) + expect(router.currentRoute.value).toMatchObject({ path: '/' }) + }) +}) diff --git a/__tests__/utils.ts b/__tests__/utils.ts index fa923435..0dd5b365 100644 --- a/__tests__/utils.ts +++ b/__tests__/utils.ts @@ -62,6 +62,7 @@ declare global { interface Global { window: JSDOM['window'] location: JSDOM['window']['location'] + history: JSDOM['window']['history'] document: JSDOM['window']['document'] before?: Function } @@ -81,6 +82,7 @@ export function createDom(options?: ConstructorOptions) { global.window = dom.window global.location = dom.window.location + global.history = dom.window.history global.document = dom.window.document return dom diff --git a/src/history/html5.ts b/src/history/html5.ts index 99d02875..58878b56 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -215,6 +215,7 @@ function useHistoryStateNavigation(base: string) { const normalized = normalizeHistoryLocation(to) const state: StateEntry = { + ...history.state, ...buildState( historyState.value.back, // keep back and forward entries but override current position @@ -222,7 +223,6 @@ function useHistoryStateNavigation(base: string) { historyState.value.forward, true ), - ...history.state, ...data, position: historyState.value.position, } -- 2.39.5