just-test
Version:
JavaScript tests runner
106 lines (93 loc) • 3.45 kB
JavaScript
/**
* NodeJS specific session runner
* - obtains the environment configuration
* - sets up object model in the NodeJS environment way
*/
import url from 'node:url';
import { workerData, Worker } from 'node:worker_threads';
import * as serverAPI from '../../server-api-service.js';
import SimpleStateService from '../../simple-state-service.js';
import { runSession } from '../../session-service.js';
import { installExecutionContext, EXECUTION_MODES } from '../../environment-config.js';
import { EVENT } from '../../../common/constants.js';
(async () => {
let sesEnvResult;
let envConfig;
const stateService = new SimpleStateService();
try {
envConfig = workerData;
const metadata = await serverAPI.getSessionMetadata(envConfig.sesId, envConfig.envId, envConfig.origin);
stateService.setSessionId(metadata.sessionId);
stateService.setEnvironmentId(metadata.id);
console.info(`registering session contents (suites/tests)...`);
await registerSession(metadata.testPaths, stateService);
const testExecutor = createNodeJSExecutor(metadata, stateService);
await runSession(stateService, testExecutor);
console.info(`collecting results...`);
sesEnvResult = stateService.getAll();
} catch (e) {
console.error(e);
console.error('session execution failed due to the previous error/s');
// TODO: the below one should probably be replaced with the error state
sesEnvResult = stateService.getAll();
} finally {
await serverAPI.reportSessionResult(envConfig.sesId, envConfig.envId, envConfig.origin, sesEnvResult);
}
})();
async function registerSession(testsResources, stateService) {
const started = globalThis.performance.now();
console.info(`fetching ${testsResources.length} test resource/s...`);
for (const tr of testsResources) {
await new Promise((resolve, reject) => {
try {
const execContext = installExecutionContext(EXECUTION_MODES.SESSION);
execContext.parentPort.on('message', testConfigs => {
for (const tc of testConfigs) {
tc.source = tr;
stateService.addTest(tc);
}
execContext.parentPort.close();
resolve();
});
import(url.pathToFileURL(tr))
.catch(e => {
console.error(`failed to process '${tr}': `, e);
reject(e);
});
} catch (e) {
console.error(`failed to process '${tr}': `, e);
reject(e);
}
});
}
const ended = globalThis.performance.now();
console.info(`... ${testsResources.length} test resource/s fetched (registration phase) in ${(ended - started).toFixed(1)}ms`);
}
function createNodeJSExecutor(sessionMetadata, stateService) {
const workerUrl = new URL('./nodejs-test-box.js', import.meta.url);
return (test) => {
const worker = new Worker(workerUrl, {
workerData: {
testId: test.id,
testSource: test.source,
coverage: sessionMetadata.coverage
}
});
worker.on('message', message => {
if (message.type === EVENT.RUN_STARTED) {
stateService.updateRunStarted(message.suiteName, message.testName);
} else if (message.type === EVENT.RUN_ENDED) {
stateService.updateRunEnded(message.suiteName, message.testName, message.run);
}
});
worker.on('error', error => {
console.error(`worker for test '${test.id}' errored: ${error}, stack: ${error.stack}`);
});
return new Promise(resolve => {
worker.on('exit', exitCode => {
console.info(`worker for test '${test.id}' exited with code ${exitCode}`);
resolve();
});
});
};
}