Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion cypress/src/cypressx/excutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export async function runTestCase(runParamFile: string): Promise<void> {
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.`);
Expand Down
134 changes: 107 additions & 27 deletions cypress/src/cypressx/utils.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand All @@ -32,7 +27,6 @@ export interface SpecResult {
attachments?: Attachment[];
}


interface JsonData {
stats: {
start: string;
Expand All @@ -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
});
});
});
}

// 判断路径是文件还是目录
Expand Down Expand Up @@ -336,8 +417,7 @@ export function createTempDirectory(): string {
return tempDirectory;
}

// 执行命令列表并上报结果,增加重试机制
const execAsync: (command: string) => Promise<{ stdout: string; stderr: string }> = promisify(exec);



export function groupTestCasesByPath(
Expand Down
2 changes: 1 addition & 1 deletion cypress/testtool.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |-
Expand Down