diff --git a/.gitignore b/.gitignore index 0e220f6..d6a1458 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,5 @@ dist # TernJS port file .tern-port + +__assets__ diff --git a/src/index.mts b/src/index.mts index f8d695d..8d09f63 100644 --- a/src/index.mts +++ b/src/index.mts @@ -11,7 +11,16 @@ import { replay } from './runner.mjs'; // noop }, async (argv) => { - await replay(argv.runCfgPath as string, argv.suiteName as string); + try { + const passed = await replay( + argv.runCfgPath as string, + argv.suiteName as string + ); + process.exit(passed ? 0 : 1); + } catch (err) { + console.error(err); + process.exit(1); + } } ) .option('runCfgPath', { diff --git a/src/runner.mts b/src/runner.mts index a76b7c2..74c2010 100644 --- a/src/runner.mts +++ b/src/runner.mts @@ -25,6 +25,7 @@ type LogEntry = { type Suite = { name: string; recording: string; + timeout?: number; }; type RunConfig = { @@ -135,20 +136,41 @@ export async function replay(runCfgPath: string, suiteName: string) { throw new Error(`Could not find suite named '${suiteName}'`); } + // saucectl suite.timeout is in nanoseconds, convert to seconds + const timeout = (suite.timeout || 0) / 1_000_000_000 || 30 * 60; // 30min default + + const timeoutPromise = new Promise((resolve) => { + setTimeout(() => { + console.error(`Job timed out after ${timeout} seconds`); + resolve(false); + }, timeout * 1000); + }); + // Validate & parse the file. const recording = parseRecording(suite.recording); - const browser = await puppeteer.launch({ - headless: false, - product: process.env.BROWSER_NAME as Product, - executablePath: process.env.BROWSER_PATH, - }); + return Promise.race([timeoutPromise, runReplay(recording)]); +} - const page = await browser.newPage(); +async function runReplay(recording: UserFlow) { + try { + const browser = await puppeteer.launch({ + headless: false, + product: process.env.BROWSER_NAME as Product, + executablePath: process.env.BROWSER_PATH, + }); + + const page = await browser.newPage(); - // Create a runner and execute the script. - const runner = await createRunner(recording, new Extension(browser, page)); + // Create a runner and execute the script. + const runner = await createRunner(recording, new Extension(browser, page)); - await runner.run(); - await browser.close(); + await runner.run(); + await browser.close(); + + return true; + } catch (e) { + console.error('Error running replay:', e.message); + return false; + } }