// Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { execFileSync } from 'child_process'; import { join } from 'path'; const ROOT_DIR = join(__dirname, '..', '..'); const ELECTRON = join( ROOT_DIR, 'node_modules', '.bin', process.platform === 'win32' ? 'electron.cmd' : 'electron' ); const MAX_RETRIES = 3; const RETRIABLE_SIGNALS = ['SIGBUS']; function launchElectron(attempt: number): string { if (attempt > MAX_RETRIES) { console.error(`Failed after ${MAX_RETRIES} retries, exiting.`); process.exit(1); } console.log(`Launching electron for tests, attempt #${attempt}...`); try { const stdout = execFileSync(ELECTRON, [ROOT_DIR], { cwd: ROOT_DIR, env: { ...process.env, // Setting NODE_ENV to test triggers main.ts to load // 'test/index.html' instead of 'background.html', which loads the tests // via `test.js` NODE_ENV: 'test', TEST_QUIT_ON_COMPLETE: 'on', }, // Since we run `.cmd` file on Windows - use shell shell: process.platform === 'win32', encoding: 'utf8', }); return stdout; } catch (error) { console.error('Status', error.status); // In testing, error.signal is null, so we need to read it from stderr const signalMatch = error.stderr.match(/exited with signal (\w+)/); const signal = error.signal || signalMatch?.[1]; console.error('Signal', signal); console.error(error.output[0] ?? ''); console.error(error.output[1] ?? ''); if (RETRIABLE_SIGNALS.includes(signal)) { return launchElectron(attempt + 1); } process.exit(1); } } const stdout = launchElectron(1); const debugMatch = stdout.matchAll(/ci:test-electron:debug=(.*)?\n/g); Array.from(debugMatch).forEach(info => { try { const args = JSON.parse(info[1]); console.log('DEBUG:', args); } catch { // this section intentionally left blank } }); const match = stdout.match(/ci:test-electron:done=(.*)?\n/); if (!match) { throw new Error('No test results were found in stdout'); } const { passed, failed, }: { passed: Array; failed: Array<{ testName: string; error: string }>; } = JSON.parse(match[1]); const total = passed.length + failed.length; for (const { testName, error } of failed) { console.error(`- ${testName}`); console.error(error); console.error(''); } console.log( `Passed ${passed.length} | Failed ${failed.length} | Total ${total}` ); if (failed.length !== 0) { process.exit(1); }