Files
PolyWeather/frontend/scripts/run-business-state-tests.mjs
T
2026-05-15 00:58:40 +08:00

107 lines
3.3 KiB
JavaScript

import fs from "node:fs";
import path from "node:path";
import vm from "node:vm";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import ts from "typescript";
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const nodeRequire = createRequire(import.meta.url);
const moduleCache = new Map();
function toWindowsSafePath(value) {
return process.platform === "win32" && value.startsWith("/")
? value.slice(1)
: value;
}
function resolveModule(specifier, fromFile) {
if (specifier.startsWith("@/")) {
return resolveFile(path.join(projectRoot, specifier.slice(2)));
}
if (specifier.startsWith(".")) {
return resolveFile(path.resolve(path.dirname(fromFile), specifier));
}
return specifier;
}
function resolveFile(basePath) {
const candidates = [
basePath,
`${basePath}.ts`,
`${basePath}.tsx`,
`${basePath}.js`,
`${basePath}.jsx`,
path.join(basePath, "index.ts"),
path.join(basePath, "index.tsx"),
path.join(basePath, "index.js"),
];
const match = candidates.find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isFile());
if (!match) {
throw new Error(`Cannot resolve module file: ${basePath}`);
}
return match;
}
function loadLocalModule(filename) {
const normalized = path.normalize(filename);
if (moduleCache.has(normalized)) return moduleCache.get(normalized).exports;
const source = fs.readFileSync(normalized, "utf8");
const transpiled = ts.transpileModule(source, {
compilerOptions: {
esModuleInterop: true,
jsx: ts.JsxEmit.ReactJSX,
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2022,
},
fileName: normalized,
}).outputText;
const module = { exports: {} };
moduleCache.set(normalized, module);
const localRequire = (specifier) => {
const resolved = resolveModule(specifier, normalized);
if (typeof resolved === "string" && path.isAbsolute(resolved)) {
return loadLocalModule(resolved);
}
return nodeRequire(specifier);
};
const wrapper = `(function (exports, require, module, __filename, __dirname) {\n${transpiled}\n})`;
const compiled = vm.runInThisContext(wrapper, { filename: normalized });
compiled(module.exports, localRequire, module, normalized, path.dirname(normalized));
return module.exports;
}
function collectTests(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
return entries.flatMap((entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) return collectTests(fullPath);
return /\.test\.tsx?$/.test(entry.name) ? [fullPath] : [];
});
}
const testsRoot = path.join(projectRoot, "components");
const testFiles = fs.existsSync(testsRoot) ? collectTests(testsRoot).sort() : [];
if (!testFiles.length) {
throw new Error(`No business state tests found under ${testsRoot}`);
}
let passed = 0;
for (const file of testFiles) {
const exported = loadLocalModule(toWindowsSafePath(file));
const run = exported.runTests || exported.default;
if (typeof run !== "function") {
throw new Error(`${file} must export runTests()`);
}
await run();
passed += 1;
console.log(`✓ ${path.relative(projectRoot, file)}`);
}
console.log(`Business state tests passed: ${passed}/${testFiles.length}`);