@@ -92,6 +92,9 @@ export const TestResultView: React.FC<{
9292 return { screenshots : [ ...screenshots ] , videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors, errorContext } ;
9393 } , [ result ] ) ;
9494
95+ const [ stepFilterText , setStepFilterText ] = React . useState ( '' ) ;
96+ React . useEffect ( ( ) => setStepFilterText ( '' ) , [ result ] ) ;
97+
9598 const prompt = useAsyncMemo ( async ( ) => {
9699 if ( report . json ( ) . options ?. noCopyPrompt )
97100 return undefined ;
@@ -134,7 +137,11 @@ export const TestResultView: React.FC<{
134137 } ) }
135138 </ AutoChip > }
136139 { ! ! result . steps . length && < AutoChip header = 'Test Steps' >
137- { result . steps . map ( ( step , i ) => < StepTreeItem key = { `step-${ i } ` } step = { step } result = { result } test = { test } depth = { 0 } /> ) }
140+ < form className = 'subnav-search step-filter' onSubmit = { e => e . preventDefault ( ) } >
141+ { icons . search ( ) }
142+ < input className = 'form-control subnav-search-input input-contrast width-full' type = 'search' spellCheck = { false } placeholder = 'Filter steps' aria-label = 'Filter steps' value = { stepFilterText } onChange = { e => setStepFilterText ( e . target . value ) } />
143+ </ form >
144+ { result . steps . map ( ( step , i ) => < StepTreeItem key = { `step-${ i } ` } step = { step } result = { result } test = { test } depth = { 0 } filterText = { stepFilterText } /> ) }
138145 </ AutoChip > }
139146
140147 { diffs . map ( ( diff , index ) =>
@@ -201,13 +208,22 @@ function pickDiffForError(error: string, diffs: ImageDiff[]): ImageDiff | undefi
201208 return diffs . find ( diff => error . includes ( diff . name ) ) ;
202209}
203210
211+ function stepMatchesFilter ( step : TestStep , filterText : string ) : boolean {
212+ if ( step . title . toLowerCase ( ) . includes ( filterText . toLowerCase ( ) ) )
213+ return true ;
214+ return step . steps . some ( s => stepMatchesFilter ( s , filterText ) ) ;
215+ }
216+
204217const StepTreeItem : React . FC < {
205218 test : TestCase ;
206219 result : TestResult ;
207220 step : TestStep ;
208221 depth : number ,
209- } > = ( { test, step, result, depth } ) => {
222+ filterText ?: string ,
223+ } > = ( { test, step, result, depth, filterText } ) => {
210224 const searchParams = useSearchParams ( ) ;
225+ if ( filterText && ! stepMatchesFilter ( step , filterText ) )
226+ return null ;
211227 return < TreeItem title = { < div aria-label = { step . title } className = 'step-title-container' >
212228 { statusIcon ( step . error || step . duration === - 1 ? 'failed' : ( step . skipped ? 'skipped' : 'passed' ) ) }
213229 < span className = 'step-title-text' >
@@ -226,9 +242,9 @@ const StepTreeItem: React.FC<{
226242 < span className = 'step-duration' > { msToString ( step . duration ) } </ span >
227243 </ div > } loadChildren = { step . steps . length || step . snippet ? ( ) => {
228244 const snippet = step . snippet ? [ < CodeSnippet testId = 'test-snippet' key = 'line' code = { step . snippet } /> ] : [ ] ;
229- const steps = step . steps . map ( ( s , i ) => < StepTreeItem key = { i } step = { s } depth = { depth + 1 } result = { result } test = { test } /> ) ;
245+ const steps = step . steps . map ( ( s , i ) => < StepTreeItem key = { i } step = { s } depth = { depth + 1 } result = { result } test = { test } filterText = { filterText } /> ) ;
230246 return snippet . concat ( steps ) ;
231- } : undefined } depth = { depth } /> ;
247+ } : undefined } depth = { depth } expandByDefault = { ! ! filterText } /> ;
232248} ;
233249
234250type WorkerLists = Map < number , { tests : TestCaseSummary [ ] , runs : number [ ] } > ;
0 commit comments