@@ -100,6 +100,21 @@ export interface EventuallyOptions {
100100 timeoutMs ?: number ;
101101 pollMs ?: number ;
102102 snapshotOptions ?: Record < string , any > ;
103+ /**
104+ * Optional: increase snapshot `limit` across retries (additive schedule).
105+ *
106+ * Useful on long/virtualized pages where a small element limit can miss targets.
107+ */
108+ snapshotLimitGrowth ?: {
109+ /** Defaults to snapshotOptions.limit if present, else 50. */
110+ startLimit ?: number ;
111+ /** Defaults to startLimit. */
112+ step ?: number ;
113+ /** Defaults to 500. */
114+ maxLimit ?: number ;
115+ /** 'only_on_fail' (default) grows on attempt>1; 'all' always applies schedule. */
116+ applyOn ?: 'only_on_fail' | 'all' ;
117+ } ;
103118 /** If set, `.eventually()` will treat snapshots below this confidence as failures and resnapshot. */
104119 minConfidence ?: number ;
105120 /** Max number of snapshot attempts to get above minConfidence before declaring exhaustion. */
@@ -133,6 +148,7 @@ export class AssertionHandle {
133148 const timeoutMs = options . timeoutMs ?? 10_000 ;
134149 const pollMs = options . pollMs ?? 250 ;
135150 const snapshotOptions = options . snapshotOptions ;
151+ const snapshotLimitGrowth = options . snapshotLimitGrowth ;
136152 const minConfidence = options . minConfidence ;
137153 const maxSnapshotAttempts = options . maxSnapshotAttempts ?? 3 ;
138154 const visionProvider = options . visionProvider ;
@@ -143,10 +159,45 @@ export class AssertionHandle {
143159 let attempt = 0 ;
144160 let snapshotAttempt = 0 ;
145161 let lastOutcome : ReturnType < Predicate > | null = null ;
162+ let snapshotLimit : number | null = null ;
163+
164+ const clampLimit = ( n : number ) : number => {
165+ if ( ! Number . isFinite ( n ) ) return 50 ;
166+ if ( n < 1 ) return 1 ;
167+ if ( n > 500 ) return 500 ;
168+ return Math . floor ( n ) ;
169+ } ;
170+
171+ const growthApplyOn = snapshotLimitGrowth ?. applyOn ?? 'only_on_fail' ;
172+ const startLimit = clampLimit (
173+ snapshotLimitGrowth ?. startLimit ??
174+ ( typeof snapshotOptions ?. limit === 'number' ? snapshotOptions . limit : 50 )
175+ ) ;
176+ const step = clampLimit ( snapshotLimitGrowth ?. step ?? startLimit ) ;
177+ const maxLimit = clampLimit ( snapshotLimitGrowth ?. maxLimit ?? 500 ) ;
178+
179+ const limitForAttempt = ( attempt1 : number ) : number => {
180+ const base = startLimit + step * Math . max ( 0 , attempt1 - 1 ) ;
181+ return clampLimit ( Math . min ( maxLimit , base ) ) ;
182+ } ;
146183
147184 while ( true ) {
148185 attempt += 1 ;
149- await this . runtime . snapshot ( snapshotOptions ) ;
186+
187+ const perAttemptOptions : Record < string , any > = { ...( snapshotOptions ?? { } ) } ;
188+ snapshotLimit = null ;
189+ if ( snapshotLimitGrowth ) {
190+ const apply =
191+ growthApplyOn === 'all' ||
192+ attempt === 1 ||
193+ ( lastOutcome !== null && lastOutcome . passed === false ) ;
194+ snapshotLimit = apply ? limitForAttempt ( attempt ) : startLimit ;
195+ perAttemptOptions . limit = snapshotLimit ;
196+ } else if ( typeof perAttemptOptions . limit === 'number' ) {
197+ snapshotLimit = clampLimit ( perAttemptOptions . limit ) ;
198+ }
199+
200+ await this . runtime . snapshot ( perAttemptOptions ) ;
150201 snapshotAttempt += 1 ;
151202
152203 const diagnostics = this . runtime . lastSnapshot ?. diagnostics ;
@@ -173,7 +224,13 @@ export class AssertionHandle {
173224 lastOutcome ,
174225 this . label ,
175226 this . required ,
176- { eventually : true , attempt, snapshot_attempt : snapshotAttempt , final : false } ,
227+ {
228+ eventually : true ,
229+ attempt,
230+ snapshot_attempt : snapshotAttempt ,
231+ snapshot_limit : snapshotLimit ,
232+ final : false ,
233+ } ,
177234 false
178235 ) ;
179236
@@ -215,6 +272,7 @@ export class AssertionHandle {
215272 eventually : true ,
216273 attempt,
217274 snapshot_attempt : snapshotAttempt ,
275+ snapshot_limit : snapshotLimit ,
218276 final : true ,
219277 vision_fallback : true ,
220278 } ,
@@ -251,6 +309,7 @@ export class AssertionHandle {
251309 eventually : true ,
252310 attempt,
253311 snapshot_attempt : snapshotAttempt ,
312+ snapshot_limit : snapshotLimit ,
254313 final : true ,
255314 exhausted : true ,
256315 } ,
@@ -271,6 +330,7 @@ export class AssertionHandle {
271330 eventually : true ,
272331 attempt,
273332 snapshot_attempt : snapshotAttempt ,
333+ snapshot_limit : snapshotLimit ,
274334 final : true ,
275335 timeout : true ,
276336 } ,
@@ -295,7 +355,13 @@ export class AssertionHandle {
295355 lastOutcome ,
296356 this . label ,
297357 this . required ,
298- { eventually : true , attempt, final : false } ,
358+ {
359+ eventually : true ,
360+ attempt,
361+ snapshot_attempt : snapshotAttempt ,
362+ snapshot_limit : snapshotLimit ,
363+ final : false ,
364+ } ,
299365 false
300366 ) ;
301367
@@ -305,7 +371,13 @@ export class AssertionHandle {
305371 lastOutcome ,
306372 this . label ,
307373 this . required ,
308- { eventually : true , attempt, final : true } ,
374+ {
375+ eventually : true ,
376+ attempt,
377+ snapshot_attempt : snapshotAttempt ,
378+ snapshot_limit : snapshotLimit ,
379+ final : true ,
380+ } ,
309381 true
310382 ) ;
311383 return true ;
@@ -317,7 +389,14 @@ export class AssertionHandle {
317389 lastOutcome ,
318390 this . label ,
319391 this . required ,
320- { eventually : true , attempt, final : true , timeout : true } ,
392+ {
393+ eventually : true ,
394+ attempt,
395+ snapshot_attempt : snapshotAttempt ,
396+ snapshot_limit : snapshotLimit ,
397+ final : true ,
398+ timeout : true ,
399+ } ,
321400 true
322401 ) ;
323402 if ( this . required ) {
0 commit comments