AI_UIAutomation/scripts/sync-ones-results.ts

133 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ONES 测试计划结果同步脚本
*
* 用法:
* npx ts-node scripts/sync-ones-results.ts --plan <PLAN_UUID> [--dry-run]
*
* 流程:
* 1. 读取 reports/.results.json (自动化执行后的结果)
* 2. 从 ONES 拉取测试计划用例列表
* 3. 优先按测试名锚点 [ONES:号(#step)] 精确匹配(支持 step 级回写)
* 无锚点的回退到用例名 LCS 模糊匹配
* 4. 反写结果到 ONES (dry-run 仅打印 payload)
*/
import * as fs from 'fs';
import * as path from 'path';
import { TestResult } from '../utils/test-reporter';
import {
fetchPlanCases,
matchResults,
buildAnchoredPayloads,
postPayloads,
OnesUpdatePayload,
} from '../utils/ones-sync';
import { execSync } from 'child_process';
const ONES_CLI = '/Users/woan/local/bin/ones';
const RESULTS_FILE = path.resolve(__dirname, '../reports/.results.json');
function parseArgs() {
const args = process.argv.slice(2);
let planUUID = '';
let dryRun = false;
for (let i = 0; i < args.length; i++) {
if (args[i] === '--plan' && args[i + 1]) {
planUUID = args[i + 1];
i++;
} else if (args[i] === '--dry-run') {
dryRun = true;
}
}
if (!planUUID) {
console.error('Usage: npx ts-node scripts/sync-ones-results.ts --plan <PLAN_UUID> [--dry-run]');
process.exit(1);
}
return { planUUID, dryRun };
}
function loadResults(): TestResult[] {
if (!fs.existsSync(RESULTS_FILE)) {
console.error(`结果文件不存在: ${RESULTS_FILE}`);
console.error('请先运行自动化测试以生成结果文件');
process.exit(1);
}
const data = JSON.parse(fs.readFileSync(RESULTS_FILE, 'utf-8'));
return data.results || [];
}
function main() {
const { planUUID, dryRun } = parseArgs();
const config = JSON.parse(execSync(`${ONES_CLI} config show`, { encoding: 'utf-8' }));
const executor = config.user_id;
console.log('='.repeat(60));
console.log(' ONES 测试计划结果同步');
console.log('='.repeat(60));
console.log(` 计划UUID: ${planUUID}`);
console.log(` 模式: ${dryRun ? '预览 (dry-run)' : '实际写入'}`);
console.log('-'.repeat(60));
// 1. 加载自动化结果
const testResults = loadResults();
console.log(`\n[1/4] 加载自动化结果: ${testResults.length}`);
const passed = testResults.filter(r => r.status === 'PASS').length;
const failed = testResults.filter(r => r.status === 'FAIL').length;
const skipped = testResults.filter(r => r.status === 'SKIP').length;
console.log(` PASS: ${passed} | FAIL: ${failed} | SKIP: ${skipped}`);
// 2. 从 ONES 拉取计划用例
console.log(`\n[2/4] 从 ONES 拉取测试计划用例 ...`);
const planCases = fetchPlanCases(planUUID);
console.log(` 计划共 ${planCases.length} 条用例`);
// 3. 匹配:锚点优先(支持 step 级),无锚点回退 LCS
console.log(`\n[3/4] 匹配 (锚点优先 + LCS 兜底) ...`);
const { payloads: anchored, unanchored } = buildAnchoredPayloads(planCases, testResults, executor);
const anchoredUUIDs = new Set(anchored.map(p => p.uuid));
const stepCount = anchored.reduce((n, p) => n + p.steps.length, 0);
console.log(` 锚点匹配: ${anchored.length} 用例 (含 ${stepCount} 个 step 级结果)`);
// LCS 兜底:仅补充锚点未覆盖的用例
const lcs = matchResults(planCases, unanchored);
const lcsPayloads: OnesUpdatePayload[] = [];
for (const [, { caseUUID, result }] of lcs) {
if (anchoredUUIDs.has(caseUUID)) continue;
lcsPayloads.push({ uuid: caseUUID, executor, note: '', result, steps: [] });
}
console.log(` LCS 兜底: ${lcsPayloads.length} 用例 (未锚点剩余 ${unanchored.length} 条)`);
const payloads = [...anchored, ...lcsPayloads];
if (payloads.length > 0) {
console.log('\n 回写预览:');
for (const p of payloads) {
const pc = planCases.find(c => c.caseUUID === p.uuid);
const icon = p.result === 'passed' ? '✓' : p.result === 'failed' ? '✗' : '○';
const stepInfo = p.steps.length ? ` (${p.steps.length} steps)` : '';
console.log(` ${icon} [${p.result}] ${pc?.caseName || p.uuid}${stepInfo}`);
}
}
// 4. 反写
if (dryRun) {
console.log(`\n[4/4] DRY-RUN将更新 ${payloads.length} 条用例,跳过实际写入`);
console.log(' 完整 payload (供核对 step 字段格式):');
console.log(JSON.stringify({ cases: payloads }, null, 2));
} else {
console.log(`\n[4/4] 反写结果到 ONES ...`);
const { success, failed: failCount } = postPayloads(planUUID, payloads);
console.log(` 成功: ${success} | 失败: ${failCount}`);
}
console.log('\n' + '='.repeat(60));
console.log(' 同步完成');
console.log('='.repeat(60));
}
main();