#!/usr/bin/env node import { exec, spawn, fork } from 'node:child_process'; import { resolve } from 'path'; import { DispatcherIpcMessages } from './constants.js'; const PARENT_DIR = resolve(__dirname, '..'); /** * Run dispatcher as child process, so we can detect when websocket server is started and adapter connected */ function runDispatcher() { const controller = new AbortController(); const { signal } = controller; const dispatcherProcess = fork('dispatcher.js', { signal, silent: true, cwd: __dirname, }); let waitForDispatcherStartedResolver: null | ((value: void | PromiseLike) => void) = null; let waitForDispatcherStartedPromise = new Promise((resolve) => { waitForDispatcherStartedResolver = resolve; }); let waitForAdapterConnectedResolver: null | ((value: void | PromiseLike) => void) = null; let waitForAdapterConnectedPromise = new Promise((resolve) => { waitForAdapterConnectedResolver = resolve; }); dispatcherProcess.on('message', (message) => { if (message.toString() === DispatcherIpcMessages.DISPATCHER_STARTED) { waitForDispatcherStartedResolver?.(); } if (message.toString() === DispatcherIpcMessages.ADAPTER_CONNECTED) { waitForAdapterConnectedResolver?.(); } }); return { controller, waitForDispatcherStarted: () => waitForDispatcherStartedPromise, waitForAdapterConnected: () => waitForAdapterConnectedPromise, }; } /** * Run adapter executable using `ADAPTER_EXECUTABLE` environment variable */ function runAdapter() { const controller = new AbortController(); const { signal } = controller; const adapterExecutable = process.env.ADAPTER_EXECUTABLE; if (!adapterExecutable) { console.log('ADAPTER_EXECUTABLE environment variable is not defined'); process.exit(1); } exec(adapterExecutable, { signal }); return { controller }; } /** * Run mocha tests */ async function runTestSuites() { const testSuiteProcess = spawn('npx', ['vitest', 'run'], { cwd: PARENT_DIR, stdio: 'inherit', shell: true, }); return new Promise<{ exitCode: number }>((resolve) => { testSuiteProcess.on('close', (code) => { console.log(`Test suite process closed with code: ${code}`); resolve({ exitCode: code ?? 0 }); }); testSuiteProcess.on('error', (error) => { console.log(`Test suite process finished with error:`, error); resolve({ exitCode: 1 }); }); }); } /** * If adapter hasn't been connected in 30 sec, we exit process */ const adapterConnectionTimeout = setTimeout(() => { console.log('Adapter has not been connected in time'); process.exit(1); }, 30_000); async function main() { console.log('Running dispatcher...'); const { controller: dispatcherController, waitForDispatcherStarted, waitForAdapterConnected } = runDispatcher(); await waitForDispatcherStarted(); console.log('Dispatcher started...'); console.log('Running adapter...'); const { controller: adapterController } = runAdapter(); await waitForAdapterConnected(); console.log('Adapter connected...'); clearTimeout(adapterConnectionTimeout); console.log('Starting test suite...'); const { exitCode } = await runTestSuites(); dispatcherController.abort(); adapterController.abort(); process.exit(exitCode); } main();