From bfbe3ad17e87fdeb241e27ffe39170cd029a2b71 Mon Sep 17 00:00:00 2001 From: hand-dot Date: Sat, 21 Mar 2026 17:52:31 +0900 Subject: [PATCH] fix: revert parallel test runner to vp sequential execution The custom parallel test runner (run-workspace-tests.mjs) caused flaky UI snapshot tests due to CPU contention during parallel execution. Co-Authored-By: Claude Opus 4.6 (1M context) --- package.json | 2 +- scripts/run-workspace-tests.mjs | 132 -------------------------------- 2 files changed, 1 insertion(+), 133 deletions(-) delete mode 100644 scripts/run-workspace-tests.mjs diff --git a/package.json b/package.json index 4aab59e3..54b85153 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "fmt:meta": "vp fmt -c .oxfmtrc.json package.json packages/cli/package.json packages/common/package.json packages/converter/package.json packages/generator/package.json packages/manipulator/package.json packages/pdf-lib/package.json packages/schemas/package.json packages/ui/package.json playground/package.json .oxlintrc.json .oxfmtrc.json playground/.oxlintrc.json vite.config.mts --write", "ci": "npm run check && npm run build && npm --prefix playground run build", "typecheck": "node packages/common/set-version.cjs && tsc -b", - "test": "node scripts/run-workspace-tests.mjs", + "test": "vp run --filter '@pdfme/*' test", "lint": "vp run --filter '@pdfme/*' lint && npm --prefix playground run lint", "lint:fix": "vp lint -c .oxlintrc.json packages/cli/src packages/common/src packages/converter/src packages/generator/src packages/manipulator/src packages/pdf-lib/src packages/schemas/src packages/ui/src --fix && npm --prefix playground run lint -- --fix" }, diff --git a/scripts/run-workspace-tests.mjs b/scripts/run-workspace-tests.mjs deleted file mode 100644 index 76a38232..00000000 --- a/scripts/run-workspace-tests.mjs +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env node - -import { spawn } from 'node:child_process'; -import { readFileSync } from 'node:fs'; -import os from 'node:os'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const repoRoot = path.resolve(__dirname, '..'); -const forwardArgs = process.argv.slice(2); - -const rootPackageJson = JSON.parse(readFileSync(path.join(repoRoot, 'package.json'), 'utf8')); -const workspaces = rootPackageJson.workspaces.filter((workspace) => - readWorkspacePackageJson(workspace).scripts?.test, -); - -const defaultConcurrency = Math.min( - workspaces.length, - Math.max(2, Math.min(4, os.availableParallelism?.() ?? os.cpus().length)), -); -const concurrency = parseConcurrency(process.env.PDFME_TEST_CONCURRENCY, defaultConcurrency); - -const failures = []; -const durations = new Map(); -let queueIndex = 0; - -console.log(`[root:test] Running ${workspaces.length} workspace test suites with concurrency ${concurrency}.`); - -await Promise.all( - Array.from({ length: concurrency }, async () => { - while (queueIndex < workspaces.length) { - const workspace = workspaces[queueIndex]; - queueIndex += 1; - const result = await runWorkspaceTest(workspace); - durations.set(workspace, result.durationMs); - if (result.code !== 0) { - failures.push(workspace); - } - } - }), -); - -for (const workspace of workspaces) { - const durationMs = durations.get(workspace) ?? 0; - const status = failures.includes(workspace) ? 'failed' : 'passed'; - console.log(`[root:test] ${workspace} ${status} in ${formatDuration(durationMs)}`); -} - -if (failures.length > 0) { - console.error(`[root:test] ${failures.length} workspace test run(s) failed.`); - process.exit(1); -} - -function readWorkspacePackageJson(workspace) { - return JSON.parse(readFileSync(path.join(repoRoot, workspace, 'package.json'), 'utf8')); -} - -function parseConcurrency(value, fallback) { - const parsed = Number(value); - if (!Number.isInteger(parsed) || parsed < 1) { - return fallback; - } - - return Math.min(parsed, workspaces.length); -} - -function runWorkspaceTest(workspace) { - const label = path.basename(workspace); - const args = ['run', 'test', '-w', workspace]; - if (forwardArgs.length > 0) { - args.push('--', ...forwardArgs); - } - - return new Promise((resolve) => { - const start = Date.now(); - const child = spawn('npm', args, { - cwd: repoRoot, - env: process.env, - stdio: ['ignore', 'pipe', 'pipe'], - }); - - pipeOutput(child.stdout, label, false); - pipeOutput(child.stderr, label, true); - - child.on('close', (code) => { - resolve({ - code: code ?? 1, - durationMs: Date.now() - start, - }); - }); - }); -} - -function pipeOutput(stream, label, useErrorStream) { - let buffer = ''; - - stream.on('data', (chunk) => { - buffer += chunk.toString(); - buffer = flushCompleteLines(buffer, label, useErrorStream); - }); - - stream.on('end', () => { - if (buffer.length > 0) { - writeLine(label, buffer, useErrorStream); - } - }); -} - -function flushCompleteLines(buffer, label, useErrorStream) { - const lines = buffer.split(/\r?\n/); - const remainder = lines.pop() ?? ''; - - for (const line of lines) { - writeLine(label, line, useErrorStream); - } - - return remainder; -} - -function writeLine(label, line, useErrorStream) { - if (line.length === 0) { - return; - } - - const writer = useErrorStream ? console.error : console.log; - writer(`[${label}] ${line}`); -} - -function formatDuration(durationMs) { - return `${(durationMs / 1000).toFixed(2)}s`; -}