testeranto
Version:
the AI powered BDD test framework for typescript projects
175 lines (174 loc) • 7.83 kB
JavaScript
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unused-vars */
import ansiC from "ansi-colors";
import fs from "fs";
import readline from "readline";
import { AppHtml } from "./utils/buildTemplates";
import { PitonoBuild } from "./PM/pitonoBuild";
import { getRunnables } from "./app/backend/utils";
import path from "path";
const { GolingvuBuild } = await import("./PM/golingvuBuild");
import webHtmlFrame from "./web.html";
// if (!process.env.GITHUB_CLIENT_ID) {
// console.error(`env var "GITHUB_CLIENT_ID" needs to be set!`);
// process.exit(-1);
// }
// if (!process.env.GITHUB_CLIENT_SECRET) {
// console.error(`env var "GITHUB_CLIENT_SECRET" needs to be set!`);
// process.exit(-1);
// }
readline.emitKeypressEvents(process.stdin);
if (process.stdin.isTTY)
process.stdin.setRawMode(true);
const testName = process.argv[2];
const mode = process.argv[3];
if (mode !== "once" && mode !== "dev") {
console.error(`The 3rd argument should be 'dev' or 'once', not '${mode}'.`);
process.exit(-1);
}
const configFilePath = process.cwd() + "/" + "testeranto.config.ts";
import(configFilePath).then(async (module) => {
const pckge = (await import(`${process.cwd()}/package.json`)).default;
const bigConfig = module.default;
const project = bigConfig.projects[testName];
if (!project) {
console.error("no project found for", testName, "in testeranto.config.ts");
process.exit(-1);
}
try {
fs.writeFileSync(`${process.cwd()}/testeranto/projects.json`, JSON.stringify(Object.keys(bigConfig.projects), null, 2));
}
catch (e) {
console.error("there was a problem");
console.error(e);
}
const rawConfig = bigConfig.projects[testName];
if (!rawConfig) {
console.error(`Project "${testName}" does not exist in the configuration.`);
console.error("Available projects:", Object.keys(bigConfig.projects));
process.exit(-1);
}
if (!rawConfig.tests) {
console.error(testName, "appears to have no tests: ", configFilePath);
console.error(`here is the config:`);
console.log(JSON.stringify(rawConfig));
process.exit(-1);
}
const config = Object.assign(Object.assign({}, rawConfig), { buildDir: process.cwd() + "/testeranto/bundles/" + testName });
console.log(ansiC.inverse("Press 'q' to initiate a graceful shutdown."));
console.log(ansiC.inverse("Press 'x' to quit forcefully."));
process.stdin.on("keypress", (str, key) => {
if (key.name === "x") {
console.log(ansiC.inverse("Shutting down forcefully..."));
process.exit(-1);
}
});
//////////////////////////////////////////////////////////////////////////////////////////////////
let pm = null;
// Start PM_Main immediately - it will handle the build processes internally
const { PM_Main } = await import("./app/backend/main");
pm = new PM_Main(config, testName, mode);
await pm.start();
fs.writeFileSync(`${process.cwd()}/testeranto/index.html`, AppHtml());
Object.keys(bigConfig.projects).forEach((projectName) => {
// console.log(`testeranto/reports/${projectName}`);
if (!fs.existsSync(`testeranto/reports/${projectName}`)) {
fs.mkdirSync(`testeranto/reports/${projectName}`);
}
fs.writeFileSync(`testeranto/reports/${projectName}/config.json`, JSON.stringify(config, null, 2));
});
const getSecondaryEndpointsPoints = (runtime) => {
const meta = (ts, st) => {
ts.forEach((t) => {
if (t[1] === runtime) {
st.add(t[0]);
}
if (Array.isArray(t[3])) {
meta(t[3], st);
}
});
return st;
};
return Array.from(meta(config.tests, new Set()));
};
// Also handle pitono endpoints for HTML generation if needed
// [...getSecondaryEndpointsPoints("python")].forEach(async (sourceFilePath) => {
// // You might want to generate specific files for pitono tests here
// console.log(`Pitono test found: ${sourceFilePath}`);
// });
Promise.resolve(Promise.all([...getSecondaryEndpointsPoints("web")].map(async (sourceFilePath) => {
const sourceFileSplit = sourceFilePath.split("/");
const sourceDir = sourceFileSplit.slice(0, -1);
const sourceFileName = sourceFileSplit[sourceFileSplit.length - 1];
const sourceFileNameMinusJs = sourceFileName
.split(".")
.slice(0, -1)
.join(".");
const htmlFilePath = path.normalize(`${process.cwd()}/testeranto/bundles/web/${testName}/${sourceDir.join("/")}/${sourceFileNameMinusJs}.html`);
const jsfilePath = `./${sourceFileNameMinusJs}.mjs`;
const cssFilePath = `./${sourceFileNameMinusJs}.css`;
return fs.promises
.mkdir(path.dirname(htmlFilePath), { recursive: true })
.then((x) => fs.writeFileSync(htmlFilePath, webHtmlFrame(jsfilePath, htmlFilePath, cssFilePath)));
})));
const { nodeEntryPoints, nodeEntryPointSidecars, webEntryPoints, webEntryPointSidecars, pureEntryPoints, pureEntryPointSidecars, pythonEntryPoints, pythonEntryPointSidecars, golangEntryPoints, golangEntryPointSidecars, } = getRunnables(config.tests, testName);
// Debug logging to check if entry points are being found
console.log("Node entry points:", Object.keys(nodeEntryPoints));
console.log("Web entry points:", Object.keys(webEntryPoints));
console.log("Pure entry points:", Object.keys(pureEntryPoints));
// Handle golang tests using GolingvuBuild
const golangTests = config.tests.filter((test) => test[1] === "golang");
const hasGolangTests = golangTests.length > 0;
if (hasGolangTests) {
const golingvuBuild = new GolingvuBuild(config, testName);
const golangEntryPoints = await golingvuBuild.build();
golingvuBuild.onBundleChange(() => {
Object.keys(golangEntryPoints).forEach((entryPoint) => {
if (pm) {
pm.addToQueue(entryPoint, "golang");
}
});
});
}
// Handle pitono (Python) tests by generating their metafiles
const pitonoTests = config.tests.filter((test) => test[1] === "python");
const hasPitonoTests = pitonoTests.length > 0;
if (hasPitonoTests) {
const pitonoBuild = new PitonoBuild(config, testName);
const pitonoEntryPoints = await pitonoBuild.build();
pitonoBuild.onBundleChange(() => {
Object.keys(pitonoEntryPoints).forEach((entryPoint) => {
if (pm) {
pm.addToQueue(entryPoint, "python");
}
});
});
}
// create the necessary folders for all tests
[
["pure", Object.keys(pureEntryPoints)],
["node", Object.keys(nodeEntryPoints)],
["web", Object.keys(webEntryPoints)],
["python", Object.keys(pythonEntryPoints)],
["golang", Object.keys(golangEntryPoints)],
].forEach(async ([runtime, keys]) => {
keys.forEach(async (k) => {
fs.mkdirSync(`testeranto/reports/${testName}/${k
.split(".")
.slice(0, -1)
.join(".")}/${runtime}`, { recursive: true });
});
});
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
process.stdin.on("keypress", (str, key) => {
if (key.name === "q") {
console.log("Testeranto is shutting down gracefully...");
if (pm) {
pm.stop();
}
else {
process.exit();
}
}
});
});