test: unify workspace test and coverage entrypoints

This commit is contained in:
DrSmoothl
2026-07-31 14:06:10 +08:00
parent 9544aa7761
commit b099dbe8cd
6 changed files with 104 additions and 5 deletions
+2
View File
@@ -130,6 +130,8 @@ pnpm test
pnpm run build:all
```
`pnpm test` 会运行所有声明了常规测试的 workspace;`pnpm test:all` 是兼容别名,`pnpm test:core` 只运行 Core 测试。原生模块测试需单独运行 `pnpm test:native`。`pnpm coverage` 会生成各常规测试 workspace 的覆盖率报告,但不设置覆盖率阻断阈值。
- 先读 [`CONTEXT.md`](CONTEXT.md) 了解模块边界与项目词汇。
- 提交代码前阅读 [`CONTRIBUTING.md`](CONTRIBUTING.md) 与 [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)。
- 开发方向与已完成事项见 [`RoadMap.md`](RoadMap.md)。
+3 -3
View File
@@ -14,13 +14,13 @@
"dev": "pnpm --filter @snowluma/core dev",
"dev:mode": "cd dist && cross-env SNOWLUMA_DEV_MODE=1 SNOWLUMA_DEV_WEBUI=1 node index.mjs",
"dev:web": "pnpm --filter webui dev",
"test": "pnpm -r --no-bail --if-present test",
"test": "pnpm --filter=\"./packages/**\" -r --no-bail --if-present test",
"test:core": "pnpm --filter @snowluma/core test",
"test:watch": "pnpm --filter @snowluma/core test:watch",
"test:common": "pnpm --filter @snowluma/common test",
"test:all": "pnpm test",
"test:native": "pnpm -r --if-present test:native",
"coverage": "pnpm --filter @snowluma/common test -- --coverage",
"test:native": "pnpm --filter=\"./packages/**\" -r --if-present test:native",
"coverage": "node tools/coverage.mjs",
"typecheck": "pnpm -r --if-present typecheck",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
+1 -1
View File
@@ -90,7 +90,7 @@
}
},
"scripts": {
"test": "vitest run",
"test": "vitest run --exclude=\"tests/native/**\"",
"test:native": "vitest run tests/native",
"bench": "vitest bench --run",
"typecheck": "tsc --noEmit"
+3 -1
View File
@@ -14,10 +14,12 @@
},
"description": "SnowLuma in-tree WebSocket implementation. Loads packages/runtime/native/websocket-<platform>-<arch>.node via process.dlopen().",
"scripts": {
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@types/node": "catalog:",
"typescript": "catalog:"
"typescript": "catalog:",
"vitest": "^4.1.4"
}
}
+3
View File
@@ -355,6 +355,9 @@ importers:
typescript:
specifier: 'catalog:'
version: 6.0.2
vitest:
specifier: ^4.1.4
version: 4.1.4(@types/node@22.13.0)(@vitest/coverage-istanbul@4.1.4)(@vitest/coverage-v8@4.1.4)(vite@8.1.0(@types/node@22.13.0)(esbuild@0.28.1)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0))
packages/webui:
dependencies:
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import { existsSync, readdirSync, readFileSync, rmSync } from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const packagesRoot = path.join(repoRoot, 'packages');
const coverageRoot = path.join(repoRoot, 'coverage');
function resolveVitestCli(manifestPath) {
const require = createRequire(manifestPath);
let directory = path.dirname(require.resolve('vitest'));
while (true) {
const packagePath = path.join(directory, 'package.json');
if (existsSync(packagePath)) {
const manifest = JSON.parse(readFileSync(packagePath, 'utf8'));
if (manifest.name === 'vitest') {
const bin = typeof manifest.bin === 'string' ? manifest.bin : manifest.bin?.vitest;
if (typeof bin !== 'string') throw new Error('Vitest does not declare a CLI binary');
return path.resolve(directory, bin);
}
}
const parent = path.dirname(directory);
if (parent === directory) throw new Error(`Could not resolve Vitest CLI from ${manifestPath}`);
directory = parent;
}
}
const workspaces = readdirSync(packagesRoot, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => {
const directory = path.join(packagesRoot, entry.name);
const manifestPath = path.join(directory, 'package.json');
if (!existsSync(manifestPath)) return null;
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
if (typeof manifest.scripts?.test !== 'string') return null;
if (typeof manifest.devDependencies?.vitest !== 'string') {
throw new Error(`${manifest.name ?? entry.name} declares tests without a package-local Vitest dependency`);
}
return {
directory,
reportDirectory: path.join(coverageRoot, entry.name),
name: manifest.name ?? entry.name,
vitestCli: resolveVitestCli(manifestPath),
};
})
.filter(Boolean);
if (workspaces.length === 0) {
throw new Error('No regular Vitest workspaces found');
}
rmSync(coverageRoot, { recursive: true, force: true });
for (const workspace of workspaces) {
console.log(`\n[coverage] ${workspace.name}`);
const result = spawnSync(
process.execPath,
[
workspace.vitestCli,
'run',
'--exclude=tests/native/**',
'--coverage',
'--coverage.provider=v8',
`--coverage.reportsDirectory=${workspace.reportDirectory}`,
'--coverage.reporter=text-summary',
'--coverage.reporter=json-summary',
'--coverage.reporter=html',
'--coverage.include=src/**/*.{js,mjs,cjs,ts,tsx}',
'--coverage.exclude=src/**/generated/**',
'--coverage.exclude=src/**/fixtures/**',
'--coverage.exclude=src/**/*.d.ts',
'--coverage.exclude=src/**/native/**',
'--coverage.exclude=dist/**',
'--coverage.exclude=**/native/**',
],
{
cwd: workspace.directory,
stdio: 'inherit',
},
);
if (result.error) throw result.error;
if (result.status !== 0) process.exit(result.status ?? 1);
}
console.log(`\n[coverage] Reports written to ${path.relative(repoRoot, coverageRoot)}/`);