diff --git a/packages/history/src/index.ts b/packages/history/src/index.ts index 0f3be8242e..7d7dc39097 100644 --- a/packages/history/src/index.ts +++ b/packages/history/src/index.ts @@ -101,7 +101,7 @@ export function createHistory(opts: { getLength: () => number pushState: (path: string, state: any) => void replaceState: (path: string, state: any) => void - go: (n: number) => void + go: (n: number, ignoreBlocker: boolean) => void back: (ignoreBlocker: boolean) => void forward: (ignoreBlocker: boolean) => void createHref: (path: string) => string @@ -204,7 +204,7 @@ export function createHistory(opts: { go: (index, navigateOpts) => { tryNavigation({ task: () => { - opts.go(index) + opts.go(index, navigateOpts?.ignoreBlocker ?? false) handleIndexChange({ type: 'GO', index }) }, navigateOpts, @@ -501,7 +501,8 @@ export function createBrowserHistory(opts?: { ignoreNextBeforeUnload = true win.history.forward() }, - go: (n) => { + go: (n, ignoreBlocker) => { + if (ignoreBlocker) skipBlockerNextPop = true nextPopIsGo = true win.history.go(n) }, diff --git a/packages/history/tests/createBrowserHistory.test.ts b/packages/history/tests/createBrowserHistory.test.ts new file mode 100644 index 0000000000..c3599bd4b2 --- /dev/null +++ b/packages/history/tests/createBrowserHistory.test.ts @@ -0,0 +1,48 @@ +import { afterEach, describe, expect, test, vi } from 'vitest' + +import { createBrowserHistory } from '../src' +import type { RouterHistory } from '../src' + +describe('createBrowserHistory', () => { + let history: RouterHistory | undefined + + afterEach(() => { + history?.destroy() + history = undefined + }) + + // Establishes two entries in the browser session history (`/one` then `/two`) + // and returns a browser history positioned on the second entry, so that a + // subsequent `go(-1)` produces a real `popstate` that reaches the blocker + // handling in `onPushPopEvent`. + function setupTwoEntries(): RouterHistory { + window.history.replaceState({ __TSR_index: 0, __TSR_key: 'one' }, '', '/one') + window.history.pushState({ __TSR_index: 1, __TSR_key: 'two' }, '', '/two') + history = createBrowserHistory() + return history + } + + describe('go respects the ignoreBlocker option', () => { + test('go(-1) runs registered blockers by default', async () => { + const history = setupTwoEntries() + const blockerFn = vi.fn(async () => true) + history.block({ blockerFn }) + + history.go(-1) + await new Promise((resolve) => setTimeout(resolve, 100)) + + expect(blockerFn).toHaveBeenCalledTimes(1) + }) + + test('go(-1, { ignoreBlocker: true }) skips registered blockers', async () => { + const history = setupTwoEntries() + const blockerFn = vi.fn(async () => true) + history.block({ blockerFn }) + + history.go(-1, { ignoreBlocker: true }) + await new Promise((resolve) => setTimeout(resolve, 100)) + + expect(blockerFn).not.toHaveBeenCalled() + }) + }) +})