/** * @license * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ // Note: can be converted to ts file once node 20 support is dropped. // Node 20 does not support --experimental-strip-types flag. import {spawn, execSync} from 'node:child_process'; import {readFile} from 'node:fs/promises'; import path from 'node:path'; import process from 'node:process'; const args = process.argv.slice(2); const userArgs = args.filter(arg => !arg.startsWith('-')); const flags = args.filter(arg => arg.startsWith('-')); const files = []; let shouldRetry = false; const retryIndex = flags.indexOf('--retry'); if (retryIndex !== -1) { shouldRetry = true; flags.splice(retryIndex, 1); } if (userArgs.length > 0) { for (const arg of userArgs) { // Map .ts files to build/ .js files let testPath = arg; if (testPath.endsWith('.ts')) { testPath = testPath.replace(/\.ts$/, '.js'); if (!testPath.startsWith('build/')) { testPath = path.join('build', testPath); } } files.push(testPath); } } else { if (flags.includes('--test-only')) { const {glob} = await import('node:fs/promises'); for await (const tsFile of glob('tests/**/*.test.ts')) { const content = await readFile(tsFile, 'utf8'); if (content.includes('.only(')) { files.push(path.join('build', tsFile.replace(/\.ts$/, '.js'))); } } if (files.length === 0) { console.warn('no files contain .only'); process.exit(0); } } else if (files.length === 0) { files.push('build/tests/**/*.test.js'); } } const nodeArgs = [ '--import', './build/tests/setup.js', '--no-warnings=ExperimentalWarning', '--test-reporter', (process.env['NODE_TEST_REPORTER'] ?? process.env['CI']) ? 'spec' : 'dot', '--test-force-exit', '--test', '--test-timeout=120000', ...flags, ...files, ]; function _installChrome(version) { try { return execSync( `npx puppeteer browsers install chrome@${version} --format "{{path}}"`, ) .toString() .trim(); } catch (e) { console.error(`Failed to install Chrome ${version}:`, e); process.exit(1); } } async function runTests(attempt) { if (attempt > 1) { console.log(`\nRun attempt ${attempt}...\n`); } return new Promise(resolve => { const child = spawn('node', nodeArgs, { stdio: 'inherit', env: { ...process.env, CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: true, CHROME_DEVTOOLS_MCP_CRASH_ON_UNCAUGHT: true, CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS: true, ...(process.env['RUNNER_DEBUG'] === '1' ? {DEBUG: 'puppeteer:*'} : {}), }, }); child.on('close', code => { resolve(code); }); }); } const maxAttempts = shouldRetry ? 3 : 1; let exitCode = 1; for (let i = 1; i <= maxAttempts; i++) { exitCode = await runTests(i); if (exitCode === 0) { break; } } process.exit(exitCode ?? 1);