1- // This wrapper script ensures that no other test is running in BrowserStack before launching the
1+ // This wrapper script waits until a BrowserStack parallel session is available before launching the
22// test command, to avoid overloading the service and making tests more flaky than necessary. This
3- // is also handled by the CI (exclusive lock on the "browserstack" resource), but it is helpful when
4- // launching tests outside of the CI.
3+ // is also handled by the CI (resource groups ), but it is helpful when launching tests outside of
4+ // the CI.
55//
66// It used to re-run the test command based on its output (in particular, when the BrowserStack
77// session failed to be created), but we observed that:
1313// after killing it. There might be a better way of prematurely aborting the test command if we need
1414// to in the future.
1515
16+ import { randomUUID } from 'node:crypto'
1617import { spawn , type ChildProcess } from 'node:child_process'
1718import browserStack from 'browserstack-local'
1819import { printLog , runMain , timeout , printError } from '../lib/executionUtils.ts'
@@ -21,9 +22,10 @@ import { browserStackRequest } from '../lib/bsUtils.ts'
2122
2223const AVAILABILITY_CHECK_DELAY = 30_000
2324const NO_OUTPUT_TIMEOUT = 5 * 60_000
24- const BS_BUILD_URL = 'https://api.browserstack.com/automate/builds .json?status=running '
25+ const BS_PLAN_URL = 'https://api.browserstack.com/automate/plan .json'
2526
2627const bsLocal = new browserStack . Local ( )
28+ const localIdentifier = `browser-sdk-${ randomUUID ( ) } `
2729
2830runMain ( async ( ) => {
2931 if ( command `git tag --points-at HEAD` . run ( ) ) {
@@ -44,15 +46,19 @@ runMain(async () => {
4446} )
4547
4648async function waitForAvailability ( ) : Promise < void > {
47- while ( await hasRunningBuild ( ) ) {
48- printLog ( 'Other build running, waiting...' )
49- await timeout ( AVAILABILITY_CHECK_DELAY )
49+ while ( await isAtCapacity ( ) ) {
50+ const jitter = Math . floor ( Math . random ( ) * 3_000 )
51+ printLog ( 'All BrowserStack sessions occupied, waiting...' )
52+ await timeout ( AVAILABILITY_CHECK_DELAY + jitter )
5053 }
5154}
5255
53- async function hasRunningBuild ( ) : Promise < boolean > {
54- const builds = ( await browserStackRequest ( BS_BUILD_URL ) ) as any [ ]
55- return builds . length > 0
56+ async function isAtCapacity ( ) : Promise < boolean > {
57+ const plan = ( await browserStackRequest ( BS_PLAN_URL ) ) as {
58+ parallel_sessions_running : number
59+ parallel_sessions_max_allowed : number
60+ }
61+ return plan . parallel_sessions_running >= plan . parallel_sessions_max_allowed
5662}
5763
5864function startBsLocal ( ) : Promise < void > {
@@ -62,8 +68,8 @@ function startBsLocal(): Promise<void> {
6268 bsLocal . start (
6369 {
6470 key : process . env . BS_ACCESS_KEY ,
71+ localIdentifier,
6572 forceLocal : true ,
66- forceKill : true ,
6773 onlyAutomate : true ,
6874 } ,
6975 ( error ?: Error ) => {
@@ -97,6 +103,7 @@ function runTests(): Promise<boolean> {
97103 ...process . env ,
98104 FORCE_COLOR : 'true' ,
99105 BROWSER_STACK : 'true' ,
106+ BROWSERSTACK_LOCAL_IDENTIFIER : localIdentifier ,
100107 } ,
101108 } )
102109
0 commit comments