1- interface TestDetails {
2- status : string ;
3- details : any ;
4- children ?: TestDetails [ ] ;
5- display_name ?: string ;
6- }
1+ import { TestStatus , FailedTestInfo , TestRun , TestDetails } from "./types.js" ;
72
8- interface TestRun {
9- hierarchy : TestDetails [ ] ;
10- pagination ?: {
11- has_next : boolean ;
12- next_page : string | null ;
13- } ;
14- }
3+ let hasNext = false ;
4+ let nextPageUrl : string | null = null ;
155
16- export interface FailedTestInfo {
17- id : string ;
18- displayName : string ;
19- }
20-
21- export async function getFailedTestIds (
6+ export async function getTestIds (
227 buildId : string ,
238 authString : string ,
9+ status ?: TestStatus ,
2410) : Promise < FailedTestInfo [ ] > {
25- const baseUrl = `https://api-automation.browserstack.com/ext/v1/builds/${ buildId } /testRuns?test_statuses=failed` ;
26- let nextUrl = baseUrl ;
11+ const baseUrl = `https://api-automation.browserstack.com/ext/v1/builds/${ buildId } /testRuns` ;
12+
13+ // Build initial URL
14+ const initialUrl = new URL ( baseUrl ) ;
15+ if ( status ) initialUrl . searchParams . set ( "test_statuses" , status ) ;
16+
17+ // Use stored nextPageUrl if available, otherwise fresh URL
18+ const requestUrl =
19+ hasNext && nextPageUrl ? nextPageUrl : initialUrl . toString ( ) ;
2720 let allFailedTests : FailedTestInfo [ ] = [ ] ;
28- let requestNumber = 0 ;
2921
3022 // Construct Basic auth header
3123 const encodedCredentials = Buffer . from ( authString ) . toString ( "base64" ) ;
3224 const authHeader = `Basic ${ encodedCredentials } ` ;
3325
3426 try {
35- while ( true ) {
36- requestNumber ++ ;
37-
38- const response = await fetch ( nextUrl , {
39- headers : {
40- Authorization : authHeader ,
41- "Content-Type" : "application/json" ,
42- } ,
43- } ) ;
44-
45- if ( ! response . ok ) {
46- throw new Error (
47- `Failed to fetch test runs: ${ response . status } ${ response . statusText } ` ,
48- ) ;
49- }
50-
51- const data = ( await response . json ( ) ) as TestRun ;
52-
53- // Extract failed IDs from current page
54- if ( data . hierarchy && data . hierarchy . length > 0 ) {
55- const currentFailedTests = extractFailedTestIds ( data . hierarchy ) ;
56- allFailedTests = allFailedTests . concat ( currentFailedTests ) ;
57- }
58-
59- // Check for pagination termination conditions
60- if ( ! data . pagination ?. has_next || ! data . pagination . next_page ) {
61- break ;
62- }
27+ const response = await fetch ( requestUrl , {
28+ headers : {
29+ Authorization : authHeader ,
30+ "Content-Type" : "application/json" ,
31+ } ,
32+ } ) ;
33+
34+ if ( ! response . ok ) {
35+ throw new Error (
36+ `Failed to fetch test runs: ${ response . status } ${ response . statusText } ` ,
37+ ) ;
38+ }
6339
64- // Safety limit to prevent runaway requests
65- if ( requestNumber >= 5 ) {
66- break ;
67- }
40+ const data = ( await response . json ( ) ) as TestRun ;
6841
69- // Prepare next request
70- nextUrl = `${ baseUrl } ?next_page=${ encodeURIComponent ( data . pagination . next_page ) } ` ;
42+ // Extract failed IDs from current page
43+ if ( data . hierarchy && data . hierarchy . length > 0 ) {
44+ allFailedTests = extractFailedTestIds ( data . hierarchy ) ;
7145 }
7246
73- // Return unique failed test IDs
47+ // Update pagination state in memory
48+ hasNext = data . pagination ?. has_next || false ;
49+ nextPageUrl =
50+ hasNext && data . pagination ?. next_page
51+ ? buildNextPageUrl ( baseUrl , status , data . pagination . next_page )
52+ : null ;
53+
54+ // Return failed test IDs from current page only
7455 return allFailedTests ;
7556 } catch ( error ) {
7657 console . error ( "Error fetching failed tests:" , error ) ;
7758 throw error ;
7859 }
7960}
8061
62+ // Helper to build next page URL safely
63+ function buildNextPageUrl (
64+ baseUrl : string ,
65+ status : TestStatus | undefined ,
66+ nextPage : string ,
67+ ) : string {
68+ const url = new URL ( baseUrl ) ;
69+ if ( status ) url . searchParams . set ( "test_statuses" , status ) ;
70+ url . searchParams . set ( "next_page" , nextPage ) ;
71+ return url . toString ( ) ;
72+ }
73+
8174// Recursive function to extract failed test IDs from hierarchy
8275function extractFailedTestIds ( hierarchy : TestDetails [ ] ) : FailedTestInfo [ ] {
8376 let failedTests : FailedTestInfo [ ] = [ ] ;
@@ -87,10 +80,7 @@ function extractFailedTestIds(hierarchy: TestDetails[]): FailedTestInfo[] {
8780 if ( node . details ?. observability_url ) {
8881 const idMatch = node . details . observability_url . match ( / d e t a i l s = ( \d + ) / ) ;
8982 if ( idMatch ) {
90- failedTests . push ( {
91- id : idMatch [ 1 ] ,
92- displayName : node . display_name || `Test ${ idMatch [ 1 ] } `
93- } ) ;
83+ failedTests . push ( { id : idMatch [ 1 ] } ) ;
9484 }
9585 }
9686 }
0 commit comments