@@ -16,82 +16,95 @@ vi.mock('@/app/o/[organizationId]/benchmark/components', () => ({
1616 CreateBenchmark : ( ) => null ,
1717} ) )
1818
19- it ( 'preserves cached benchmarks through a failed next page and retries without losing the first page' , async ( ) => {
20- authClientMockFns . mockUseSession . mockReturnValue ( {
21- data : { user : { id : 'owner' } } ,
22- isPending : false ,
23- error : null ,
24- } )
25- vi . useFakeTimers ( )
26- vi . stubGlobal ( 'IS_REACT_ACT_ENVIRONMENT' , true )
27- const client = new QueryClient ( { defaultOptions : { queries : { retry : false } } } )
28- const container = document . createElement ( 'div' )
29- document . body . append ( container )
30- const root = createRoot ( container )
31- const record = ( id : string ) => ( {
32- id,
33- name : id ,
34- organizationId : 'org' ,
35- userId : 'owner' ,
36- runAsUserId : 'owner' ,
37- sourceWorkspaceId : 'workspace' ,
38- version : 1 ,
39- runningStage : null ,
40- leaseExpiresAt : null ,
41- attemptId : null ,
42- plannerChatId : null ,
43- error : null ,
44- createdAt : '2026-01-01T00:00:00.000Z' ,
45- updatedAt : '2026-01-01T00:00:00.000Z' ,
46- } )
47- const fetcher = vi
48- . fn ( )
49- . mockResolvedValueOnce (
50- jsonResponse ( { benchmarks : [ record ( 'First benchmark' ) ] , nextCursor : 'next' } )
51- )
52- . mockResolvedValueOnce ( jsonResponse ( { error : 'Temporary failure' } , 503 ) )
53- . mockResolvedValueOnce (
54- jsonResponse ( { benchmarks : [ record ( 'Older benchmark' ) ] , nextCursor : null } )
55- )
56- vi . stubGlobal ( 'fetch' , fetcher )
57- const settle = async ( action : ( ) => void ) => {
58- await act ( async ( ) => action ( ) )
59- await act ( async ( ) => {
60- await flushMicrotasks ( )
61- await vi . advanceTimersByTimeAsync ( 1 )
19+ it . each ( [ false , true ] ) (
20+ 'recovers initial failure %s and paginates without losing cached benchmarks' ,
21+ async ( initialFailure ) => {
22+ authClientMockFns . mockUseSession . mockReturnValue ( {
23+ data : { user : { id : 'owner' } } ,
24+ isPending : false ,
25+ error : null ,
6226 } )
63- }
64- const openButtons = ( ) =>
65- [ ...container . querySelectorAll < HTMLButtonElement > ( 'button' ) ] . filter (
66- ( button ) => button . textContent === 'Open'
67- )
68- const olderButton = ( ) =>
69- [ ...container . querySelectorAll < HTMLButtonElement > ( 'button' ) ] . find ( ( button ) =>
70- / o l d e r b e n c h m a r k s / i. test ( button . textContent ?? '' )
71- )
72- try {
73- await settle ( ( ) =>
74- root . render (
75- < QueryClientProvider client = { client } >
76- < NuqsTestingAdapter hasMemory >
77- < Benchmark organizationId = 'org' runAsUserId = 'owner' canPlan />
78- </ NuqsTestingAdapter >
79- </ QueryClientProvider >
27+ vi . useFakeTimers ( )
28+ vi . stubGlobal ( 'IS_REACT_ACT_ENVIRONMENT' , true )
29+ const client = new QueryClient ( { defaultOptions : { queries : { retry : false } } } )
30+ const container = document . createElement ( 'div' )
31+ document . body . append ( container )
32+ const root = createRoot ( container )
33+ const record = ( id : string ) => ( {
34+ id,
35+ name : id ,
36+ organizationId : 'org' ,
37+ userId : 'owner' ,
38+ runAsUserId : 'owner' ,
39+ sourceWorkspaceId : 'workspace' ,
40+ version : 1 ,
41+ runningStage : null ,
42+ leaseExpiresAt : null ,
43+ attemptId : null ,
44+ plannerChatId : null ,
45+ error : null ,
46+ createdAt : '2026-01-01T00:00:00.000Z' ,
47+ updatedAt : '2026-01-01T00:00:00.000Z' ,
48+ } )
49+ const fetcher = vi . fn ( )
50+ if ( initialFailure )
51+ fetcher . mockResolvedValueOnce ( jsonResponse ( { error : 'Temporary failure' } , 503 ) )
52+ fetcher
53+ . mockResolvedValueOnce (
54+ jsonResponse ( { benchmarks : [ record ( 'First benchmark' ) ] , nextCursor : 'next' } )
55+ )
56+ . mockResolvedValueOnce ( jsonResponse ( { error : 'Temporary failure' } , 503 ) )
57+ . mockResolvedValueOnce (
58+ jsonResponse ( { benchmarks : [ record ( 'Older benchmark' ) ] , nextCursor : null } )
59+ )
60+ vi . stubGlobal ( 'fetch' , fetcher )
61+ const settle = async ( action : ( ) => void ) => {
62+ await act ( async ( ) => action ( ) )
63+ await act ( async ( ) => {
64+ await flushMicrotasks ( )
65+ await vi . advanceTimersByTimeAsync ( 1 )
66+ } )
67+ }
68+ const openButtons = ( ) =>
69+ [ ...container . querySelectorAll < HTMLButtonElement > ( 'button' ) ] . filter (
70+ ( button ) => button . textContent === 'Open'
71+ )
72+ const olderButton = ( ) =>
73+ [ ...container . querySelectorAll < HTMLButtonElement > ( 'button' ) ] . find ( ( button ) =>
74+ / o l d e r b e n c h m a r k s / i. test ( button . textContent ?? '' )
75+ )
76+ try {
77+ await settle ( ( ) =>
78+ root . render (
79+ < QueryClientProvider client = { client } >
80+ < NuqsTestingAdapter hasMemory >
81+ < Benchmark organizationId = 'org' runAsUserId = 'owner' canPlan />
82+ </ NuqsTestingAdapter >
83+ </ QueryClientProvider >
84+ )
8085 )
81- )
82- expect ( openButtons ( ) ) . toHaveLength ( 1 )
83- await settle ( ( ) => olderButton ( ) ?. click ( ) )
84- expect ( container . querySelector ( '[role="alert"]' ) ) . not . toBeNull ( )
85- expect ( openButtons ( ) ) . toHaveLength ( 1 )
86- expect ( olderButton ( ) ?. disabled ) . toBe ( false )
87- await settle ( ( ) => olderButton ( ) ?. click ( ) )
88- expect ( openButtons ( ) ) . toHaveLength ( 2 )
89- expect ( container . querySelector ( '[role="alert"]' ) ) . toBeNull ( )
90- expect ( olderButton ( ) ) . toBeUndefined ( )
91- } finally {
92- await act ( async ( ) => root . unmount ( ) )
93- client . clear ( )
94- container . remove ( )
95- vi . useRealTimers ( )
86+ if ( initialFailure ) {
87+ expect ( openButtons ( ) ) . toHaveLength ( 0 )
88+ await settle ( ( ) =>
89+ [ ...container . querySelectorAll < HTMLButtonElement > ( 'button' ) ]
90+ . find ( ( button ) => button . textContent === 'Retry' )
91+ ?. click ( )
92+ )
93+ }
94+ expect ( openButtons ( ) ) . toHaveLength ( 1 )
95+ await settle ( ( ) => olderButton ( ) ?. click ( ) )
96+ expect ( container . querySelector ( '[role="alert"]' ) ) . not . toBeNull ( )
97+ expect ( openButtons ( ) ) . toHaveLength ( 1 )
98+ expect ( olderButton ( ) ?. disabled ) . toBe ( false )
99+ await settle ( ( ) => olderButton ( ) ?. click ( ) )
100+ expect ( openButtons ( ) ) . toHaveLength ( 2 )
101+ expect ( container . querySelector ( '[role="alert"]' ) ) . toBeNull ( )
102+ expect ( olderButton ( ) ) . toBeUndefined ( )
103+ } finally {
104+ await act ( async ( ) => root . unmount ( ) )
105+ client . clear ( )
106+ container . remove ( )
107+ vi . useRealTimers ( )
108+ }
96109 }
97- } )
110+ )
0 commit comments