From f48b94976941b2fe95eee5e725a4e24f2be82645 Mon Sep 17 00:00:00 2001 From: ambzhang Date: Tue, 11 Nov 2025 15:01:58 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=20executeCommand?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E5=AE=9E=E6=97=B6=E8=BE=93=E5=87=BA?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E6=89=A7=E8=A1=8C=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cypress/src/cypressx/excutor.ts | 2 +- cypress/src/cypressx/utils.ts | 134 +++++++++++++++++++++++++------- 2 files changed, 108 insertions(+), 28 deletions(-) diff --git a/cypress/src/cypressx/excutor.ts b/cypress/src/cypressx/excutor.ts index 94517bd..2bd3528 100644 --- a/cypress/src/cypressx/excutor.ts +++ b/cypress/src/cypressx/excutor.ts @@ -31,7 +31,7 @@ export async function runTestCase(runParamFile: string): Promise { const command = generateCommands(path, testcases, jsonName); // 执行命令,解析用例生成的 JSON 文件,上报结果 - const { success } = await executeCommand(command); + const { success } = await executeCommand(command, true); if (!success) { log.warn(`Command execution failed for ${path}. Check logs for details.`); diff --git a/cypress/src/cypressx/utils.ts b/cypress/src/cypressx/utils.ts index 324b5f0..da5b1bf 100644 --- a/cypress/src/cypressx/utils.ts +++ b/cypress/src/cypressx/utils.ts @@ -1,10 +1,8 @@ import * as process from "process"; import * as child_process from "child_process"; -import * as util from "util"; import * as fs from "fs"; import * as os from "os"; import * as path from "path"; -import { promisify } from 'util'; import log from 'testsolar-oss-sdk/src/testsolar_sdk/logger'; import { TestCase } from "testsolar-oss-sdk/src/testsolar_sdk/model/test"; @@ -18,9 +16,6 @@ import { Attachment, } from "testsolar-oss-sdk/src/testsolar_sdk/model/testresult"; -const exec = util.promisify(child_process.exec); - - export interface SpecResult { result: string; duration: number; @@ -32,7 +27,6 @@ export interface SpecResult { attachments?: Attachment[]; } - interface JsonData { stats: { start: string; @@ -56,27 +50,114 @@ interface JsonData { }>; } - -// 执行命令并返回结果 -export async function executeCommand(command: string): Promise<{ success: boolean; stdout: string; stderr: string }> { - try { - const { stdout, stderr } = await execAsync(command); - return { success: true, stdout, stderr }; - } catch (error) { - const typedError = error as Error & { stdout: string; stderr: string }; - // 记录错误日志 - log.error(`Error executing command: ${command}`); - log.error(`Error message: ${typedError.message}`); - log.error(`stdout: ${typedError.stdout}`); - log.error(`stderr: ${typedError.stderr}`); +/** + * 执行命令并实时输出日志 + * @param command 要执行的命令 + * @param realtime 是否实时输出(默认 true) + * @returns Promise<{ success: boolean; stdout: string; stderr: string }> + */ +export async function executeCommand( + command: string, + realtime: boolean = true +): Promise<{ success: boolean; stdout: string; stderr: string }> { + return new Promise((resolve) => { + log.info(`执行命令: ${command}`); - // 返回错误信息,而不是抛出错误 - return { - success: false, - stdout: typedError.stdout || '', - stderr: typedError.stderr || typedError.message + // 使用 spawn 而不是 exec,支持流式输出 + const child = child_process.spawn(command, { + shell: true, + stdio: realtime ? ['inherit', 'pipe', 'pipe'] : 'pipe', + env: process.env + }); + + let stdout = ''; + let stderr = ''; + let resolved = false; + + // 安全的 resolve 函数,防止多次调用 + const resolveOnce = (result: { success: boolean; stdout: string; stderr: string }) => { + if (!resolved) { + resolved = true; + resolve(result); + } }; - } + + // 监听标准输出 + if (child.stdout) { + child.stdout.on('data', (data: Buffer) => { + const text = data.toString(); + stdout += text; + + if (realtime) { + // 方案1:直接输出(保留 Cypress 的原始格式和颜色) + process.stdout.write(text); + + // 方案2:或者按行输出到日志系统(二选一) + // const lines = text.split('\n'); + // for (const line of lines) { + // if (line.trim()) { + // log.info(line); + // } + // } + } + }); + } + + // 监听错误输出 + if (child.stderr) { + child.stderr.on('data', (data: Buffer) => { + const text = data.toString(); + stderr += text; + + if (realtime) { + // 直接输出到 stderr + process.stderr.write(text); + + // 或者按行记录日志 + // const lines = text.split('\n'); + // for (const line of lines) { + // if (line.trim()) { + // if (line.toLowerCase().includes('warning')) { + // log.warn(line); + // } else { + // log.error(line); + // } + // } + // } + } + }); + } + + // 监听进程退出 + child.on('close', (code: number | null) => { + const success = code === 0; + const exitMsg = success + ? `命令执行成功 (exit code: ${code})` + : `命令执行失败 (exit code: ${code})`; + + if (success) { + log.info(exitMsg); + } else { + log.error(exitMsg); + } + + resolveOnce({ + success, + stdout, + stderr + }); + }); + + // 监听错误事件 + child.on('error', (error: Error) => { + log.error(`命令执行出错: ${error.message}`); + resolveOnce({ + success: false, + stdout, + stderr: stderr || error.message + }); + }); + }); } // 判断路径是文件还是目录 @@ -336,8 +417,7 @@ export function createTempDirectory(): string { return tempDirectory; } -// 执行命令列表并上报结果,增加重试机制 -const execAsync: (command: string) => Promise<{ stdout: string; stderr: string }> = promisify(exec); + export function groupTestCasesByPath( From dd91e5961ade250e1d3437c61973292167f4ce10 Mon Sep 17 00:00:00 2001 From: ambzhang Date: Tue, 11 Nov 2025 15:02:21 +0800 Subject: [PATCH 2/2] committed --- cypress/package.json | 2 +- cypress/testtool.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/package.json b/cypress/package.json index 74dbfe6..55afe8a 100644 --- a/cypress/package.json +++ b/cypress/package.json @@ -1,6 +1,6 @@ { "name": "testsolar-oss-cypress", - "version": "0.0.8", + "version": "0.0.9", "description": "TestSolar support for cypress tool", "main": "main.js", "scripts": { diff --git a/cypress/testtool.yaml b/cypress/testtool.yaml index 6454d46..7bc8059 100644 --- a/cypress/testtool.yaml +++ b/cypress/testtool.yaml @@ -2,7 +2,7 @@ schemaVersion: 1.0 name: cypress nameZh: Cypress自动化测试 lang: javascript -version: '0.0.8' +version: '0.0.9' defaultBaseImage: node:18 langType: INTERPRETED description: |-