1- import { describe , expect , it , vi , beforeEach } from "vitest" ;
1+ import { describe , expect , it , vi , beforeEach , afterEach } from "vitest" ;
22import { renderHook , act } from "@testing-library/react" ;
33
4- const replaceMock = vi . fn ( ) ;
54let currentParams = new URLSearchParams ( ) ;
65
76vi . mock ( "next/navigation" , ( ) => ( {
8- useRouter : ( ) => ( { replace : replaceMock } ) ,
97 usePathname : ( ) => "/" ,
108 useSearchParams : ( ) => currentParams ,
119} ) ) ;
1210
1311import { useSelection } from "./useSelection" ;
1412
13+ // Selections are committed via the native History API (Next.js syncs useSearchParams
14+ // with window.history.replaceState) rather than the router's navigation machinery,
15+ // which breaks on deep-linked static-export loads.
16+ const replaceState = vi . spyOn ( window . history , "replaceState" ) ;
17+ const replacedUrl = ( ) => replaceState . mock . calls [ 0 ] [ 2 ] as string ;
18+
1519describe ( "useSelection" , ( ) => {
1620 beforeEach ( ( ) => {
17- replaceMock . mockClear ( ) ;
21+ replaceState . mockClear ( ) ;
1822 currentParams = new URLSearchParams ( ) ;
1923 } ) ;
2024
25+ afterEach ( ( ) => {
26+ replaceState . mockReset ( ) ;
27+ } ) ;
28+
2129 it ( "exposes defaults when no params are set" , ( ) => {
2230 const { result } = renderHook ( ( ) => useSelection ( ) ) ;
2331 expect ( result . current . benchmark ) . toBeNull ( ) ;
@@ -39,11 +47,11 @@ describe("useSelection", () => {
3947 expect ( result . current . julia ) . toBe ( "1.12" ) ;
4048 } ) ;
4149
42- it ( "select() writes params via router.replace and drops defaults" , ( ) => {
50+ it ( "select() writes params via history.replaceState and drops defaults" , ( ) => {
4351 const { result } = renderHook ( ( ) => useSelection ( ) ) ;
4452 act ( ( ) => result . current . select ( { benchmark : "basic/coin_toss" , metric : "all" } ) ) ;
45- expect ( replaceMock ) . toHaveBeenCalledTimes ( 1 ) ;
46- const url = replaceMock . mock . calls [ 0 ] [ 0 ] as string ;
53+ expect ( replaceState ) . toHaveBeenCalledTimes ( 1 ) ;
54+ const url = replacedUrl ( ) ;
4755 expect ( url ) . toContain ( "b=basic%2Fcoin_toss" ) ;
4856 expect ( url ) . not . toContain ( "m=" ) ; // "all" is the default -> omitted
4957 } ) ;
@@ -52,7 +60,7 @@ describe("useSelection", () => {
5260 currentParams = new URLSearchParams ( "b=basic%2Fkalman&m=cold_run_ms" ) ;
5361 const { result } = renderHook ( ( ) => useSelection ( ) ) ;
5462 act ( ( ) => result . current . select ( { benchmark : null } ) ) ;
55- const url = replaceMock . mock . calls [ 0 ] [ 0 ] as string ;
63+ const url = replacedUrl ( ) ;
5664 expect ( url ) . not . toContain ( "b=" ) ;
5765 expect ( url ) . toContain ( "m=cold_run_ms" ) ; // untouched params survive
5866 } ) ;
0 commit comments