/** * 测试门禁:扫描 tests/ 下所有 *.test.js,全部通过才能正常返回 0 * * 用法: * node scripts/test-gate.js * 或: npm test * * 工作机制: * 1) 扫描 tests/ 目录,找到所有 *.test.js 文件 * 2) 用 node --test 跑每个文件,任一文件非 0 退出则整体失败 * 3) 输出汇总:总数 / 通过 / 失败 / 失败的文件列表 * * 兼容性: * node 18+ 自带 test runner(--test)。低于 18 会给出明确错误并退出非 0。 */ 'use strict'; const fs = require('node:fs'); const path = require('node:path'); const { spawnSync } = require('node:child_process'); const ROOT = path.resolve(__dirname, '..'); const TESTS_DIR = path.join(ROOT, 'tests'); function listTestFiles(dir) { if (!fs.existsSync(dir)) return []; return fs.readdirSync(dir) .filter(function (f) { return f.endsWith('.test.js'); }) .map(function (f) { return path.join(dir, f); }) .sort(); } function checkNodeVersion() { const major = Number(process.versions.node.split('.')[0]); if (major < 18) { console.error('[test-gate] 需要 node 18+(自带 --test),当前 node 版本:', process.version); process.exit(1); } } function runTestFile(testFile) { console.log('\n[test-gate] === 运行测试文件:', path.relative(ROOT, testFile), '==='); const result = spawnSync( process.execPath, ['--test', testFile], { stdio: 'inherit', cwd: ROOT }, ); return { file: path.relative(ROOT, testFile), ok: result.status === 0, status: result.status, }; } function main() { checkNodeVersion(); const files = listTestFiles(TESTS_DIR); if (files.length === 0) { console.error('[test-gate] tests/ 下没有任何 *.test.js 文件'); process.exit(1); } console.log('[test-gate] 找到', files.length, '个测试文件'); const results = files.map(runTestFile); const failed = results.filter(function (r) { return !r.ok; }); console.log('\n[test-gate] =============== 汇总 ==============='); console.log(' 总数:', results.length); console.log(' 通过:', results.length - failed.length); console.log(' 失败:', failed.length); if (failed.length > 0) { console.log(' 失败文件:'); failed.forEach(function (r) { console.log(' -', r.file, '(退出码', r.status + ')'); }); console.log('\n[test-gate] ❌ 测试未全部通过,不允许继续'); process.exit(1); } console.log('\n[test-gate] ✅ 全部测试通过'); process.exit(0); } main();