/* * Worker runtime * * The worker executes the tests by called `node` on them. Since each test * is an self-contained executable file, the worker can run each of them, * collect output and relay it back to the runner process via IPC. * * Each worker process is responsible for as many test files as the runner * decides to assign it and files assigned to the worker are only * touched by the worker assigned to them. */ import path from 'path' import { getContext, spawnProcess } from './utils' function formatMessage(results: string, failed: boolean): string { return JSON.stringify({ results, failed }) } /* * Entrypoint for the worker. * * Retrieves paths assigned to the worker from the arguments passed when * calling the worker runtime and spawns processes to run the test file * pointed at by each of the paths. * * This will spawn one process per file and each process will communicate back * to the worker's parent process as it finishes. * * If the `TS` flag is passed, the worker runs the test file using ts-node * for Typescript compatibility. */ async function work() { if (process?.send === undefined) throw Error('No process global found') const tsMode = Boolean(process.env.TS === '1') const [, workerRuntime, ...assignedTestFiles] = process.argv const context = getContext(workerRuntime, tsMode) const extraArgs: Array = [] if (context.ts) extraArgs.push('--transpile-only') const runtime = context.ts ? 'ts-node' : 'node' await Promise.all( assignedTestFiles.map( (testFilePath) => new Promise((resolve, reject) => { spawnProcess(runtime, [...extraArgs, path.resolve(testFilePath)], { onClose: (code) => { resolve(code) }, onStdoutData: (message) => { process?.send?.(formatMessage(message.trim(), message.includes('FAILED'))) }, }) }), ), ) } work().catch((e) => { console.log(e) })