qat-cli 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +507 -387
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +259 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +259 -146
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
|
|
9
9
|
// src/cli.ts
|
|
10
10
|
import { Command } from "commander";
|
|
11
|
-
import
|
|
11
|
+
import chalk13 from "chalk";
|
|
12
12
|
|
|
13
13
|
// src/commands/init.ts
|
|
14
|
-
import
|
|
14
|
+
import chalk3 from "chalk";
|
|
15
15
|
import inquirer from "inquirer";
|
|
16
16
|
import ora from "ora";
|
|
17
17
|
import fs6 from "fs";
|
|
@@ -843,6 +843,15 @@ var NoopAIProvider = class {
|
|
|
843
843
|
};
|
|
844
844
|
|
|
845
845
|
// src/ai/openai-provider.ts
|
|
846
|
+
import chalk2 from "chalk";
|
|
847
|
+
function isDebug() {
|
|
848
|
+
return process.env.QAT_DEBUG === "true";
|
|
849
|
+
}
|
|
850
|
+
function debugLog(tag, ...args) {
|
|
851
|
+
if (!isDebug()) return;
|
|
852
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
|
|
853
|
+
console.error(chalk2.gray(`[DEBUG ${timestamp}] [${tag}]`), ...args);
|
|
854
|
+
}
|
|
846
855
|
var OpenAICompatibleProvider = class {
|
|
847
856
|
constructor(config) {
|
|
848
857
|
this.capabilities = {
|
|
@@ -856,25 +865,20 @@ var OpenAICompatibleProvider = class {
|
|
|
856
865
|
this.baseUrl = config.baseUrl || this.getDefaultBaseUrl(config.provider);
|
|
857
866
|
}
|
|
858
867
|
async generateTest(req) {
|
|
868
|
+
debugLog("GENERATE", `type=${req.type} target=${req.target}`);
|
|
859
869
|
const systemPrompt = this.buildGenerateTestSystemPrompt(req);
|
|
860
870
|
const userPrompt = this.buildGenerateTestUserPrompt(req);
|
|
861
871
|
const content = await this.chat(systemPrompt, userPrompt);
|
|
862
|
-
|
|
872
|
+
const result = this.parseGenerateTestResponse(content);
|
|
873
|
+
debugLog("GENERATE", `done code=${result.code.length}chars confidence=${result.confidence}`);
|
|
874
|
+
return result;
|
|
863
875
|
}
|
|
864
876
|
async analyzeResult(req) {
|
|
865
|
-
const systemPrompt = `\
|
|
866
|
-
\u8F93\u51FA\
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
const
|
|
870
|
-
const failed = r.suites.flatMap((s) => s.tests.filter((t) => t.status === "failed"));
|
|
871
|
-
return `\u7C7B\u578B: ${r.type}, \u72B6\u6001: ${r.status}, \u8017\u65F6: ${r.duration}ms, \u5931\u8D25\u7528\u4F8B: ${failed.length}`;
|
|
872
|
-
}).join("\n");
|
|
873
|
-
const errorDetails = req.errorLogs?.join("\n") || req.testResults.flatMap((r) => r.suites.flatMap((s) => s.tests.filter((t) => t.status === "failed" && t.error))).map((t) => `[${t.name}] ${t.error?.message}`).join("\n") || "\u65E0\u9519\u8BEF\u8BE6\u60C5";
|
|
874
|
-
const userPrompt = `\u6D4B\u8BD5\u7ED3\u679C:
|
|
875
|
-
${resultSummary}
|
|
876
|
-
|
|
877
|
-
\u9519\u8BEF\u8BE6\u60C5:
|
|
877
|
+
const systemPrompt = `\u6D4B\u8BD5\u5206\u6790\u4E13\u5BB6\u3002\u627E\u95EE\u9898\u6839\u56E0\uFF0C\u7ED9\u53EF\u64CD\u4F5C\u5EFA\u8BAE\u3002
|
|
878
|
+
\u8F93\u51FA:1.\u6458\u8981(1-3\u53E5) 2.\u5EFA\u8BAE\u5217\u8868`;
|
|
879
|
+
const failed = req.testResults.flatMap((r) => r.suites.flatMap((s) => s.tests.filter((t) => t.status === "failed")));
|
|
880
|
+
const errorDetails = req.errorLogs?.join("\n") || failed.map((t) => `[${t.name}] ${t.error?.message}`).join("\n") || "\u65E0";
|
|
881
|
+
const userPrompt = `\u5931\u8D25:${failed.length}
|
|
878
882
|
${errorDetails}`;
|
|
879
883
|
const content = await this.chat(systemPrompt, userPrompt);
|
|
880
884
|
return {
|
|
@@ -884,23 +888,22 @@ ${errorDetails}`;
|
|
|
884
888
|
};
|
|
885
889
|
}
|
|
886
890
|
async suggestFix(error) {
|
|
887
|
-
const systemPrompt = `\
|
|
888
|
-
\
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
const userPrompt = `\u9519\u8BEF\u4FE1\u606F: ${error.message}
|
|
893
|
-
${error.stack ? `\u5806\u6808: ${error.stack}` : ""}
|
|
894
|
-
${error.expected ? `\u671F\u671B\u503C: ${error.expected}` : ""}
|
|
895
|
-
${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
|
|
891
|
+
const systemPrompt = `\u4EE3\u7801\u4FEE\u590D\u4E13\u5BB6\u3002\u7ED9\u51FA:1.\u95EE\u9898\u5B9A\u4F4D 2.\u4FEE\u590D\u65B9\u6848 3.\u793A\u4F8B\u4EE3\u7801`;
|
|
892
|
+
const userPrompt = `\u9519\u8BEF:${error.message}${error.stack ? `
|
|
893
|
+
\u5806\u6808:${error.stack}` : ""}${error.expected ? `
|
|
894
|
+
\u671F\u671B:${error.expected}` : ""}${error.actual ? `
|
|
895
|
+
\u5B9E\u9645:${error.actual}` : ""}`;
|
|
896
896
|
const content = await this.chat(systemPrompt, userPrompt);
|
|
897
897
|
return content.split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("\u2022") || l.trim().match(/^\d+\./)).map((l) => l.replace(/^[-•\d.]+\s*/, "").trim()).filter(Boolean);
|
|
898
898
|
}
|
|
899
899
|
async reviewTest(req) {
|
|
900
|
+
debugLog("REVIEW", `target=${req.target} type=${req.testType}`);
|
|
900
901
|
const systemPrompt = this.buildReviewTestSystemPrompt(req);
|
|
901
902
|
const userPrompt = this.buildReviewTestUserPrompt(req);
|
|
902
903
|
const content = await this.chat(systemPrompt, userPrompt);
|
|
903
|
-
|
|
904
|
+
const result = this.parseReviewTestResponse(content);
|
|
905
|
+
debugLog("REVIEW", `approved=${result.approved} score=${result.score} feedback=${result.feedback}`);
|
|
906
|
+
return result;
|
|
904
907
|
}
|
|
905
908
|
// ─── 内部方法 ──────────────────────────────────────────────
|
|
906
909
|
/**
|
|
@@ -964,8 +967,9 @@ ${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
|
|
|
964
967
|
return { ok: false, message: `\u8FDE\u63A5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`, latencyMs };
|
|
965
968
|
}
|
|
966
969
|
}
|
|
967
|
-
async chat(systemPrompt, userPrompt) {
|
|
970
|
+
async chat(systemPrompt, userPrompt, retries = 2) {
|
|
968
971
|
const url = `${this.baseUrl}/chat/completions`;
|
|
972
|
+
const useStream = isDebug();
|
|
969
973
|
const body = {
|
|
970
974
|
model: this.model,
|
|
971
975
|
messages: [
|
|
@@ -975,101 +979,189 @@ ${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
|
|
|
975
979
|
temperature: 0.3,
|
|
976
980
|
max_tokens: 4096
|
|
977
981
|
};
|
|
982
|
+
if (useStream) {
|
|
983
|
+
body.stream = true;
|
|
984
|
+
}
|
|
978
985
|
const headers = {
|
|
979
986
|
"Content-Type": "application/json"
|
|
980
987
|
};
|
|
981
988
|
if (this.apiKey) {
|
|
982
989
|
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
983
990
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
991
|
+
debugLog("REQUEST", `POST ${url}`);
|
|
992
|
+
debugLog("REQUEST", `model=${this.model} stream=${useStream}`);
|
|
993
|
+
debugLog("SYSTEM", systemPrompt.length > 500 ? `${systemPrompt.slice(0, 500)}...` : systemPrompt);
|
|
994
|
+
debugLog("USER", userPrompt.length > 1e3 ? `${userPrompt.slice(0, 1e3)}...` : userPrompt);
|
|
995
|
+
let lastError = null;
|
|
996
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
997
|
+
try {
|
|
998
|
+
if (attempt > 0) {
|
|
999
|
+
debugLog("RETRY", `\u7B2C${attempt}\u6B21\u91CD\u8BD5...`);
|
|
1000
|
+
}
|
|
1001
|
+
const response = await fetch(url, {
|
|
1002
|
+
method: "POST",
|
|
1003
|
+
headers,
|
|
1004
|
+
body: JSON.stringify(body),
|
|
1005
|
+
signal: AbortSignal.timeout(12e4)
|
|
1006
|
+
// 120s timeout
|
|
1007
|
+
});
|
|
1008
|
+
if (!response.ok) {
|
|
1009
|
+
const text = await response.text().catch(() => "");
|
|
1010
|
+
if ((response.status === 429 || response.status >= 500) && attempt < retries) {
|
|
1011
|
+
const delay = Math.min(1e3 * Math.pow(2, attempt), 8e3);
|
|
1012
|
+
debugLog("RETRY", `HTTP ${response.status}, ${delay}ms\u540E\u91CD\u8BD5`);
|
|
1013
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1014
|
+
lastError = new Error(`AI API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${text.slice(0, 200)}`);
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
throw new Error(`AI API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${text.slice(0, 500)}`);
|
|
1018
|
+
}
|
|
1019
|
+
if (useStream && response.body) {
|
|
1020
|
+
const content = await this.readStream(response.body);
|
|
1021
|
+
debugLog("RESPONSE", content.length > 500 ? `${content.slice(0, 500)}...` : content);
|
|
1022
|
+
return content;
|
|
1023
|
+
}
|
|
1024
|
+
const data = await response.json();
|
|
1025
|
+
if (!data.choices?.[0]?.message?.content) {
|
|
1026
|
+
throw new Error("AI API \u8FD4\u56DE\u7A7A\u54CD\u5E94");
|
|
1027
|
+
}
|
|
1028
|
+
debugLog("RESPONSE", `tokens: prompt=${data.usage?.prompt_tokens} completion=${data.usage?.completion_tokens} total=${data.usage?.total_tokens}`);
|
|
1029
|
+
debugLog("RESPONSE", data.choices[0].message.content.length > 500 ? `${data.choices[0].message.content.slice(0, 500)}...` : data.choices[0].message.content);
|
|
1030
|
+
return data.choices[0].message.content;
|
|
1031
|
+
} catch (error) {
|
|
1032
|
+
if (error instanceof Error && error.name === "TimeoutError" && attempt < retries) {
|
|
1033
|
+
debugLog("TIMEOUT", `\u7B2C${attempt}\u6B21\u8D85\u65F6\uFF0C\u91CD\u8BD5\u4E2D...`);
|
|
1034
|
+
lastError = error;
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
throw error;
|
|
1038
|
+
}
|
|
994
1039
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1040
|
+
throw lastError || new Error("AI API \u8BF7\u6C42\u5931\u8D25");
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* 读取 SSE 流式响应,实时输出内容
|
|
1044
|
+
*/
|
|
1045
|
+
async readStream(body) {
|
|
1046
|
+
const chunks = [];
|
|
1047
|
+
const decoder = new TextDecoder();
|
|
1048
|
+
const reader = body.getReader();
|
|
1049
|
+
let buffer = "";
|
|
1050
|
+
let lineCount = 0;
|
|
1051
|
+
try {
|
|
1052
|
+
while (true) {
|
|
1053
|
+
const { done, value } = await reader.read();
|
|
1054
|
+
if (done) break;
|
|
1055
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1056
|
+
const lines = buffer.split("\n");
|
|
1057
|
+
buffer = lines.pop() || "";
|
|
1058
|
+
for (const line of lines) {
|
|
1059
|
+
const trimmed = line.trim();
|
|
1060
|
+
if (!trimmed || trimmed === "data: [DONE]") continue;
|
|
1061
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
1062
|
+
try {
|
|
1063
|
+
const json = JSON.parse(trimmed.slice(6));
|
|
1064
|
+
const delta = json.choices?.[0]?.delta?.content;
|
|
1065
|
+
if (delta) {
|
|
1066
|
+
chunks.push(delta);
|
|
1067
|
+
lineCount++;
|
|
1068
|
+
process.stderr.write(chalk2.gray(delta));
|
|
1069
|
+
if (lineCount % 20 === 0) {
|
|
1070
|
+
process.stderr.write("\n");
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (json.choices?.[0]?.finish_reason === "stop") {
|
|
1074
|
+
debugLog("STREAM", "\u5B8C\u6210");
|
|
1075
|
+
}
|
|
1076
|
+
} catch {
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
} finally {
|
|
1081
|
+
reader.releaseLock();
|
|
998
1082
|
}
|
|
999
|
-
|
|
1083
|
+
if (chunks.length > 0) {
|
|
1084
|
+
process.stderr.write("\n");
|
|
1085
|
+
}
|
|
1086
|
+
return chunks.join("");
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* 压缩源码:保留签名和关键逻辑,剔除注释、空行、样式块
|
|
1090
|
+
* 目标:在保留准确性的前提下减少 token 消耗
|
|
1091
|
+
*/
|
|
1092
|
+
compressSourceCode(code, maxLength = 3e3) {
|
|
1093
|
+
let compressed = code;
|
|
1094
|
+
compressed = compressed.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "");
|
|
1095
|
+
compressed = compressed.replace(/<template[^>]*>([\s\S]*?)<\/template>/gi, (_match, content) => {
|
|
1096
|
+
return `<template>${content.replace(/\s*(?:class|style)\s*=\s*["'][^"']*["']/gi, "")}</template>`;
|
|
1097
|
+
});
|
|
1098
|
+
compressed = compressed.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
1099
|
+
compressed = compressed.replace(/(^|[^:])(\/\/.*$)/gm, "$1");
|
|
1100
|
+
compressed = compressed.replace(/\n\s*\n\s*\n/g, "\n\n");
|
|
1101
|
+
compressed = compressed.split("\n").map((line) => line.trimEnd()).join("\n").trim();
|
|
1102
|
+
if (compressed.length > maxLength) {
|
|
1103
|
+
const scriptMatch = compressed.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
1104
|
+
const templateMatch = compressed.match(/<template[^>]*>([\s\S]*?)<\/template>/i);
|
|
1105
|
+
if (scriptMatch) {
|
|
1106
|
+
let scriptPart = scriptMatch[1].trim();
|
|
1107
|
+
scriptPart = compressFunctionBodies(scriptPart, maxLength * 0.7);
|
|
1108
|
+
const templatePart = templateMatch ? `<template>${templateMatch[1].replace(/\s+/g, " ").trim().slice(0, 300)}...</template>` : "";
|
|
1109
|
+
compressed = `${templatePart}
|
|
1110
|
+
<script${scriptMatch[0].match(/<script[^>]*>/)?.[0]?.slice(7) || ">"}>${scriptPart}</script>`;
|
|
1111
|
+
} else {
|
|
1112
|
+
compressed = compressFunctionBodies(compressed, maxLength);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return compressed;
|
|
1000
1116
|
}
|
|
1001
1117
|
buildGenerateTestSystemPrompt(req) {
|
|
1002
1118
|
const typeMap = {
|
|
1003
|
-
unit: "\u5355\u5143\u6D4B\u8BD5
|
|
1004
|
-
component: "\u7EC4\u4EF6\u6D4B\u8BD5
|
|
1005
|
-
e2e: "E2E\
|
|
1006
|
-
api: "API\
|
|
1007
|
-
visual: "\u89C6\u89C9\u56DE\u5F52\u6D4B\u8BD5
|
|
1008
|
-
performance: "\u6027\u80FD\u6D4B\u8BD5
|
|
1119
|
+
unit: "\u5355\u5143\u6D4B\u8BD5(Vitest)",
|
|
1120
|
+
component: "\u7EC4\u4EF6\u6D4B\u8BD5(Vitest+@vue/test-utils)",
|
|
1121
|
+
e2e: "E2E\u6D4B\u8BD5(Playwright)",
|
|
1122
|
+
api: "API\u6D4B\u8BD5(Vitest+fetch)",
|
|
1123
|
+
visual: "\u89C6\u89C9\u56DE\u5F52\u6D4B\u8BD5(Playwright)",
|
|
1124
|
+
performance: "\u6027\u80FD\u6D4B\u8BD5(Playwright)"
|
|
1009
1125
|
};
|
|
1010
|
-
return `\u4F60\u662F\
|
|
1011
|
-
\
|
|
1012
|
-
1. \u53EA\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801\uFF0C\u4E0D\u8981\u591A\u4F59\u7684\u89E3\u91CA
|
|
1013
|
-
2. \u4EE3\u7801\u5FC5\u987B\u53EF\u76F4\u63A5\u8FD0\u884C\uFF0C\u5305\u542B\u6240\u6709\u5FC5\u8981\u7684 import
|
|
1014
|
-
3. \u6D4B\u8BD5\u7528\u4F8B\u8986\u76D6\uFF1A\u6B63\u5E38\u8DEF\u5F84\u3001\u8FB9\u754C\u6761\u4EF6\u3001\u9519\u8BEF\u5904\u7406
|
|
1015
|
-
4. \u4F7F\u7528\u4E2D\u6587\u63CF\u8FF0 it/test \u5757\u540D\u79F0
|
|
1016
|
-
5. Vue \u7EC4\u4EF6\u6D4B\u8BD5\u4F7F\u7528 @vue/test-utils \u7684 mount
|
|
1017
|
-
6. \u5982\u679C\u6709 props/emits \u4FE1\u606F\uFF0C\u52A1\u5FC5\u9488\u5BF9\u6BCF\u4E2A prop \u548C emit \u751F\u6210\u6D4B\u8BD5`;
|
|
1126
|
+
return `\u4F60\u662F\u524D\u7AEF\u6D4B\u8BD5\u5DE5\u7A0B\u5E08\uFF0C\u7F16\u5199${typeMap[req.type] || req.type}\u3002
|
|
1127
|
+
\u89C4\u5219:1.\u53EA\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801 2.\u5305\u542B\u6240\u6709import 3.\u8986\u76D6\u6B63\u5E38/\u8FB9\u754C/\u9519\u8BEF 4.it\u540D\u7528\u4E2D\u6587 5.Vue\u7528mount 6.\u5FC5\u6D4B\u6BCF\u4E2Aprop\u548Cemit`;
|
|
1018
1128
|
}
|
|
1019
1129
|
buildGenerateTestUserPrompt(req) {
|
|
1020
1130
|
const importPath = this.computeTestImportPath(req.type, req.target);
|
|
1021
|
-
let prompt = `\
|
|
1022
|
-
\u76EE\u6807\u6587\u4EF6: ${req.target}
|
|
1023
|
-
\u6D4B\u8BD5\u6587\u4EF6\u5C06\u653E\u5728: ${this.getTestOutputDir(req.type)}/
|
|
1024
|
-
\u6B63\u786E\u7684 import \u8DEF\u5F84: ${importPath}
|
|
1025
|
-
|
|
1026
|
-
\u91CD\u8981\uFF1Aimport \u8BED\u53E5\u4E2D\u5FC5\u987B\u4F7F\u7528\u4E0A\u8FF0\u6B63\u786E\u7684\u76F8\u5BF9\u8DEF\u5F84 ${importPath}\uFF0C\u4E0D\u8981\u4F7F\u7528 ${req.target} \u6216\u5176\u4ED6\u8DEF\u5F84\uFF01
|
|
1131
|
+
let prompt = `\u4E3A${req.target}\u751F\u6210${req.type}\u6D4B\u8BD5\u3002import\u8DEF\u5F84:${importPath}
|
|
1027
1132
|
`;
|
|
1028
1133
|
if (req.analysis) {
|
|
1029
|
-
|
|
1134
|
+
const parts = [];
|
|
1030
1135
|
if (req.analysis.exports?.length > 0) {
|
|
1031
|
-
|
|
1032
|
-
${
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;
|
|
1036
|
-
}).join("\n")}
|
|
1037
|
-
`;
|
|
1136
|
+
parts.push(`\u5BFC\u51FA:${req.analysis.exports.map((e) => {
|
|
1137
|
+
const p = e.params?.length ? `(${e.params.join(",")})` : "";
|
|
1138
|
+
return `${e.isAsync ? "async " : ""}${e.name}${p}[${e.kind}]`;
|
|
1139
|
+
}).join(",")}`);
|
|
1038
1140
|
}
|
|
1039
1141
|
if (req.analysis.props?.length) {
|
|
1040
|
-
|
|
1041
|
-
${req.analysis.props.map(
|
|
1042
|
-
(p) => ` - ${p.name}: ${p.type}${p.required ? " (\u5FC5\u586B)" : " (\u53EF\u9009)"}`
|
|
1043
|
-
).join("\n")}
|
|
1044
|
-
`;
|
|
1142
|
+
parts.push(`Props:${req.analysis.props.map((p) => `${p.name}:${p.type}${p.required ? "!" : "?"}`).join(",")}`);
|
|
1045
1143
|
}
|
|
1046
1144
|
if (req.analysis.emits?.length) {
|
|
1047
|
-
|
|
1048
|
-
${req.analysis.emits.map(
|
|
1049
|
-
(e) => ` - ${e.name}${e.params?.length ? `(${e.params.join(", ")})` : ""}`
|
|
1050
|
-
).join("\n")}
|
|
1051
|
-
`;
|
|
1145
|
+
parts.push(`Emits:${req.analysis.emits.map((e) => `${e.name}(${e.params?.join(",") || ""})`).join(",")}`);
|
|
1052
1146
|
}
|
|
1053
1147
|
if (req.analysis.methods?.length) {
|
|
1054
|
-
|
|
1055
|
-
`;
|
|
1148
|
+
parts.push(`Methods:${req.analysis.methods.join(",")}`);
|
|
1056
1149
|
}
|
|
1057
1150
|
if (req.analysis.computed?.length) {
|
|
1058
|
-
|
|
1059
|
-
`;
|
|
1151
|
+
parts.push(`Computed:${req.analysis.computed.join(",")}`);
|
|
1060
1152
|
}
|
|
1153
|
+
prompt += parts.join("\n") + "\n";
|
|
1061
1154
|
}
|
|
1062
1155
|
if (req.context) {
|
|
1063
1156
|
prompt += `
|
|
1064
|
-
\u6E90\u7801
|
|
1065
|
-
\`\`\`
|
|
1066
|
-
${req.context}
|
|
1157
|
+
\u6E90\u7801:
|
|
1158
|
+
\`\`\`
|
|
1159
|
+
${this.compressSourceCode(req.context)}
|
|
1067
1160
|
\`\`\`
|
|
1068
1161
|
`;
|
|
1069
1162
|
}
|
|
1070
1163
|
if (req.framework) {
|
|
1071
|
-
prompt +=
|
|
1072
|
-
\u6846\u67B6: ${req.framework}`;
|
|
1164
|
+
prompt += `\u6846\u67B6:${req.framework}`;
|
|
1073
1165
|
}
|
|
1074
1166
|
return prompt;
|
|
1075
1167
|
}
|
|
@@ -1099,20 +1191,6 @@ ${req.context}
|
|
|
1099
1191
|
}
|
|
1100
1192
|
return `${prefix}${cleanPath}`;
|
|
1101
1193
|
}
|
|
1102
|
-
/**
|
|
1103
|
-
* 获取测试输出目录
|
|
1104
|
-
*/
|
|
1105
|
-
getTestOutputDir(testType) {
|
|
1106
|
-
const dirMap = {
|
|
1107
|
-
unit: "tests/unit",
|
|
1108
|
-
component: "tests/component",
|
|
1109
|
-
e2e: "tests/e2e",
|
|
1110
|
-
api: "tests/api",
|
|
1111
|
-
visual: "tests/visual",
|
|
1112
|
-
performance: "tests/e2e"
|
|
1113
|
-
};
|
|
1114
|
-
return dirMap[testType] || "tests/unit";
|
|
1115
|
-
}
|
|
1116
1194
|
parseGenerateTestResponse(content) {
|
|
1117
1195
|
const codeBlockMatch = content.match(/```(?:typescript|ts|javascript|js)?\s*\n([\s\S]*?)```/);
|
|
1118
1196
|
const code = codeBlockMatch ? codeBlockMatch[1].trim() : content.replace(/^(?:```[\s\S]*?\n)?/, "").replace(/\n?```$/, "").trim();
|
|
@@ -1124,69 +1202,49 @@ ${req.context}
|
|
|
1124
1202
|
};
|
|
1125
1203
|
}
|
|
1126
1204
|
buildReviewTestSystemPrompt(_req) {
|
|
1127
|
-
return `\u4F60\u662F\
|
|
1128
|
-
|
|
1129
|
-
\
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
\u8F93\u51FA\u683C\u5F0F\uFF08\u4E25\u683C\u9075\u5B88\uFF09\uFF1A
|
|
1137
|
-
APPROVED: true \u6216 false
|
|
1138
|
-
SCORE: 0.0 \u5230 1.0 \u4E4B\u95F4\u7684\u8BC4\u5206
|
|
1139
|
-
FEEDBACK: \u4E00\u53E5\u8BDD\u5BA1\u8BA1\u610F\u89C1
|
|
1140
|
-
ISSUES: \u95EE\u9898\u5217\u8868\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF0C\u683C\u5F0F "- \u95EE\u9898\u63CF\u8FF0"\uFF09
|
|
1141
|
-
SUGGESTIONS: \u6539\u8FDB\u5EFA\u8BAE\u5217\u8868\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF0C\u683C\u5F0F "- \u5EFA\u8BAE\u63CF\u8FF0"\uFF09`;
|
|
1205
|
+
return `\u4F60\u662F\u6D4B\u8BD5\u5BA1\u8BA1\u4E13\u5BB6\u3002\u5BA1\u67E5\u6D4B\u8BD5\u4EE3\u7801\u662F\u5426\u51C6\u786E\u3002
|
|
1206
|
+
\u6807\u51C6:1.\u7C7B\u578B\u5339\u914D 2.\u8986\u76D6\u6838\u5FC3\u5BFC\u51FA/props/emits 3.\u65AD\u8A00\u6709\u6548(\u975E\u6C38\u771F) 4.import\u6B63\u786E 5.\u53EF\u8FD0\u884C
|
|
1207
|
+
\u8F93\u51FA:
|
|
1208
|
+
APPROVED:true\u6216false
|
|
1209
|
+
SCORE:0.0-1.0
|
|
1210
|
+
FEEDBACK:\u4E00\u53E5\u8BDD
|
|
1211
|
+
ISSUES:- \u95EE\u9898(\u6BCF\u884C\u4E00\u4E2A)
|
|
1212
|
+
SUGGESTIONS:- \u5EFA\u8BAE(\u6BCF\u884C\u4E00\u4E2A)`;
|
|
1142
1213
|
}
|
|
1143
1214
|
buildReviewTestUserPrompt(req) {
|
|
1144
1215
|
const importPath = this.computeTestImportPath(req.testType, req.target);
|
|
1145
|
-
let prompt = `\
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
\
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1216
|
+
let prompt = `\u5BA1\u67E5\u6D4B\u8BD5\u4EE3\u7801\u3002\u88AB\u6D4B:${req.target} \u7C7B\u578B:${req.testType} import\u8DEF\u5F84:${importPath}
|
|
1217
|
+
`;
|
|
1218
|
+
prompt += `
|
|
1219
|
+
\u6E90\u7801:
|
|
1220
|
+
\`\`\`
|
|
1221
|
+
${this.compressSourceCode(req.sourceCode, 2e3)}
|
|
1222
|
+
\`\`\`
|
|
1223
|
+
`;
|
|
1224
|
+
prompt += `
|
|
1225
|
+
\u6D4B\u8BD5\u4EE3\u7801:
|
|
1154
1226
|
\`\`\`
|
|
1155
|
-
|
|
1156
|
-
--- \u751F\u6210\u7684\u6D4B\u8BD5\u4EE3\u7801 ---
|
|
1157
|
-
\`\`\`typescript
|
|
1158
1227
|
${req.testCode}
|
|
1159
1228
|
\`\`\``;
|
|
1160
1229
|
if (req.analysis) {
|
|
1161
|
-
|
|
1230
|
+
const parts = [];
|
|
1162
1231
|
if (req.analysis.exports?.length > 0) {
|
|
1163
|
-
|
|
1164
|
-
\u5BFC\u51FA\u9879:
|
|
1165
|
-
${req.analysis.exports.map((e) => {
|
|
1166
|
-
const params = e.params?.length ? `(${e.params.join(", ")})` : "";
|
|
1167
|
-
const asyncFlag = e.isAsync ? "async " : "";
|
|
1168
|
-
return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;
|
|
1169
|
-
}).join("\n")}`;
|
|
1232
|
+
parts.push(`\u5BFC\u51FA:${req.analysis.exports.map((e) => `${e.isAsync ? "async " : ""}${e.name}(${e.params?.join(",") || ""})[${e.kind}]`).join(",")}`);
|
|
1170
1233
|
}
|
|
1171
1234
|
if (req.analysis.props?.length) {
|
|
1172
|
-
|
|
1173
|
-
Props:
|
|
1174
|
-
${req.analysis.props.map(
|
|
1175
|
-
(p) => ` - ${p.name}: ${p.type}${p.required ? " (\u5FC5\u586B)" : " (\u53EF\u9009)"}`
|
|
1176
|
-
).join("\n")}`;
|
|
1235
|
+
parts.push(`Props:${req.analysis.props.map((p) => `${p.name}:${p.type}${p.required ? "!" : "?"}`).join(",")}`);
|
|
1177
1236
|
}
|
|
1178
1237
|
if (req.analysis.emits?.length) {
|
|
1238
|
+
parts.push(`Emits:${req.analysis.emits.map((e) => `${e.name}(${e.params?.join(",") || ""})`).join(",")}`);
|
|
1239
|
+
}
|
|
1240
|
+
if (parts.length > 0) {
|
|
1179
1241
|
prompt += `
|
|
1180
|
-
|
|
1181
|
-
${req.analysis.emits.map(
|
|
1182
|
-
(e) => ` - ${e.name}${e.params?.length ? `(${e.params.join(", ")})` : ""}`
|
|
1183
|
-
).join("\n")}`;
|
|
1242
|
+
\u5206\u6790:${parts.join("|")}`;
|
|
1184
1243
|
}
|
|
1185
1244
|
}
|
|
1186
1245
|
if (req.generationDescription) {
|
|
1187
1246
|
prompt += `
|
|
1188
|
-
|
|
1189
|
-
\u751F\u6210\u8005\u8BF4\u660E: ${req.generationDescription}`;
|
|
1247
|
+
\u8BF4\u660E:${req.generationDescription}`;
|
|
1190
1248
|
}
|
|
1191
1249
|
return prompt;
|
|
1192
1250
|
}
|
|
@@ -1246,6 +1304,61 @@ ${req.analysis.emits.map(
|
|
|
1246
1304
|
return urlMap[provider] || "https://api.openai.com/v1";
|
|
1247
1305
|
}
|
|
1248
1306
|
};
|
|
1307
|
+
function compressFunctionBodies(code, maxLength) {
|
|
1308
|
+
const lines = code.split("\n");
|
|
1309
|
+
const result = [];
|
|
1310
|
+
let depth = 0;
|
|
1311
|
+
let fnDepth = 0;
|
|
1312
|
+
let inFunction = false;
|
|
1313
|
+
let braceBalance = 0;
|
|
1314
|
+
let capturedLines = 0;
|
|
1315
|
+
for (const line of lines) {
|
|
1316
|
+
const trimmed = line.trim();
|
|
1317
|
+
for (const ch of trimmed) {
|
|
1318
|
+
if (ch === "{") {
|
|
1319
|
+
braceBalance++;
|
|
1320
|
+
depth++;
|
|
1321
|
+
}
|
|
1322
|
+
if (ch === "}") {
|
|
1323
|
+
braceBalance--;
|
|
1324
|
+
depth = Math.max(0, depth - 1);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
const isFnSignature = /^(export\s+)?(async\s+)?function\s|=>\s*\{|=>\s*$/m.test(trimmed) || /^(const|let|var)\s+\w+\s*=\s*(async\s+)?(\([^)]*\)|[^=])\s*=>/.test(trimmed);
|
|
1328
|
+
if (isFnSignature && !inFunction) {
|
|
1329
|
+
inFunction = true;
|
|
1330
|
+
fnDepth = depth;
|
|
1331
|
+
result.push(line);
|
|
1332
|
+
capturedLines++;
|
|
1333
|
+
continue;
|
|
1334
|
+
}
|
|
1335
|
+
if (inFunction) {
|
|
1336
|
+
const isReturn = trimmed.startsWith("return ");
|
|
1337
|
+
const isBranch = /^(if|else|switch|case|try|catch|finally|for|while)/.test(trimmed);
|
|
1338
|
+
const isClosing = trimmed === "}" && depth <= fnDepth - 1;
|
|
1339
|
+
if (isClosing) {
|
|
1340
|
+
result.push(line);
|
|
1341
|
+
inFunction = false;
|
|
1342
|
+
capturedLines++;
|
|
1343
|
+
} else if (isReturn || isBranch) {
|
|
1344
|
+
result.push(trimmed.length > 120 ? `${trimmed.slice(0, 120)}...` : line);
|
|
1345
|
+
capturedLines++;
|
|
1346
|
+
} else if (trimmed.startsWith("//") || trimmed === "") {
|
|
1347
|
+
} else if (capturedLines < maxLength / 30) {
|
|
1348
|
+
result.push(trimmed.length > 100 ? `${trimmed.slice(0, 100)}...` : line);
|
|
1349
|
+
capturedLines++;
|
|
1350
|
+
}
|
|
1351
|
+
} else {
|
|
1352
|
+
result.push(line);
|
|
1353
|
+
capturedLines++;
|
|
1354
|
+
}
|
|
1355
|
+
if (result.join("\n").length > maxLength) {
|
|
1356
|
+
result.push("// ... (truncated)");
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return result.join("\n");
|
|
1361
|
+
}
|
|
1249
1362
|
|
|
1250
1363
|
// src/ai/provider.ts
|
|
1251
1364
|
var providerRegistry = /* @__PURE__ */ new Map();
|
|
@@ -1794,7 +1907,7 @@ function registerInitCommand(program2) {
|
|
|
1794
1907
|
try {
|
|
1795
1908
|
await executeInit(options);
|
|
1796
1909
|
} catch (error) {
|
|
1797
|
-
console.error(
|
|
1910
|
+
console.error(chalk3.red(`
|
|
1798
1911
|
\u2717 ${error instanceof Error ? error.message : String(error)}
|
|
1799
1912
|
`));
|
|
1800
1913
|
process.exit(1);
|
|
@@ -1816,19 +1929,19 @@ async function executeInit(options) {
|
|
|
1816
1929
|
}
|
|
1817
1930
|
]);
|
|
1818
1931
|
if (!proceed) {
|
|
1819
|
-
console.log(
|
|
1932
|
+
console.log(chalk3.gray("\n \u5DF2\u53D6\u6D88\u521D\u59CB\u5316\n"));
|
|
1820
1933
|
return;
|
|
1821
1934
|
}
|
|
1822
1935
|
}
|
|
1823
1936
|
let globalAI = loadGlobalAIConfig();
|
|
1824
1937
|
if (!globalAI) {
|
|
1825
|
-
console.log(
|
|
1938
|
+
console.log(chalk3.cyan(" AI \u6A21\u578B\u914D\u7F6E (\u9996\u6B21\u4F7F\u7528\u9700\u914D\u7F6E\uFF0C\u4E4B\u540E\u53EF\u901A\u8FC7 qat change \u4FEE\u6539)\n"));
|
|
1826
1939
|
globalAI = await promptAIConfig();
|
|
1827
1940
|
saveGlobalAIConfig(globalAI);
|
|
1828
|
-
console.log(
|
|
1941
|
+
console.log(chalk3.gray(` \u914D\u7F6E\u5DF2\u4FDD\u5B58\u81F3 ${getAIConfigPath()}
|
|
1829
1942
|
`));
|
|
1830
1943
|
} else {
|
|
1831
|
-
console.log(
|
|
1944
|
+
console.log(chalk3.green(` \u2713 \u5F53\u524D AI \u6A21\u578B: ${chalk3.white(globalAI.model)} @ ${chalk3.gray(globalAI.baseUrl)} (${maskApiKey(globalAI.apiKey)})
|
|
1832
1945
|
`));
|
|
1833
1946
|
}
|
|
1834
1947
|
const aiConfig = toAIConfig(globalAI);
|
|
@@ -1837,10 +1950,10 @@ async function executeInit(options) {
|
|
|
1837
1950
|
try {
|
|
1838
1951
|
const result = await testAIConnection(aiConfig);
|
|
1839
1952
|
if (result.ok) {
|
|
1840
|
-
testSpinner.succeed(`AI \u8FDE\u901A\u6B63\u5E38 ${
|
|
1953
|
+
testSpinner.succeed(`AI \u8FDE\u901A\u6B63\u5E38 ${chalk3.gray(`${globalAI.model} (${result.latencyMs}ms)`)}`);
|
|
1841
1954
|
} else {
|
|
1842
1955
|
testSpinner.fail(`AI \u8FDE\u901A\u5F02\u5E38: ${result.message}`);
|
|
1843
|
-
console.log(
|
|
1956
|
+
console.log(chalk3.yellow(" \u53EF\u8FD0\u884C qat change \u4FEE\u6539 AI \u914D\u7F6E\u3002"));
|
|
1844
1957
|
}
|
|
1845
1958
|
} catch (error) {
|
|
1846
1959
|
testSpinner.fail(`AI \u8FDE\u901A\u6D4B\u8BD5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1861,7 +1974,7 @@ async function executeInit(options) {
|
|
|
1861
1974
|
}
|
|
1862
1975
|
]);
|
|
1863
1976
|
if (!overwrite) {
|
|
1864
|
-
console.log(
|
|
1977
|
+
console.log(chalk3.gray(" \u4FDD\u7559\u73B0\u6709\u914D\u7F6E\u6587\u4EF6\uFF0C\u7EE7\u7EED\u540E\u7EED\u6B65\u9AA4..."));
|
|
1865
1978
|
configPath = existingConfigPath;
|
|
1866
1979
|
} else {
|
|
1867
1980
|
const fileSpinner = ora("\u6B63\u5728\u8986\u76D6\u914D\u7F6E\u6587\u4EF6...").start();
|
|
@@ -1896,10 +2009,10 @@ async function executeInit(options) {
|
|
|
1896
2009
|
const mockFilePath = path6.join(process.cwd(), mockDir, "auto-generated.json");
|
|
1897
2010
|
if (!fs6.existsSync(mockFilePath)) {
|
|
1898
2011
|
fs6.writeFileSync(mockFilePath, JSON.stringify(mockRoutes, null, 2), "utf-8");
|
|
1899
|
-
console.log(
|
|
2012
|
+
console.log(chalk3.green(` \u81EA\u52A8\u53D1\u73B0 ${apiCalls.length} \u4E2A API \u63A5\u53E3\uFF0C\u5DF2\u751F\u6210 Mock \u8DEF\u7531`));
|
|
1900
2013
|
}
|
|
1901
2014
|
} else {
|
|
1902
|
-
console.log(
|
|
2015
|
+
console.log(chalk3.gray(" \u672A\u53D1\u73B0 API \u8C03\u7528\uFF0C\u5DF2\u751F\u6210\u793A\u4F8B Mock \u8DEF\u7531"));
|
|
1903
2016
|
}
|
|
1904
2017
|
}
|
|
1905
2018
|
const srcDir = config.project?.srcDir || "src";
|
|
@@ -1907,7 +2020,7 @@ async function executeInit(options) {
|
|
|
1907
2020
|
const utilities = discoverUtilityFiles(process.cwd(), srcDir);
|
|
1908
2021
|
const totalFiles = components.length + utilities.length;
|
|
1909
2022
|
if (totalFiles > 0) {
|
|
1910
|
-
console.log(
|
|
2023
|
+
console.log(chalk3.cyan(`
|
|
1911
2024
|
\u53D1\u73B0 ${totalFiles} \u4E2A\u53EF\u6D4B\u8BD5\u6587\u4EF6 (${components.length} \u7EC4\u4EF6, ${utilities.length} \u5DE5\u5177/\u670D\u52A1)`));
|
|
1912
2025
|
}
|
|
1913
2026
|
displayResult(configPath, createdDirs, totalFiles, projectInfo);
|
|
@@ -1994,8 +2107,8 @@ function buildProjectConfig(projectInfo) {
|
|
|
1994
2107
|
};
|
|
1995
2108
|
}
|
|
1996
2109
|
function displayProjectInfo(info) {
|
|
1997
|
-
console.log(
|
|
1998
|
-
console.log(
|
|
2110
|
+
console.log(chalk3.cyan("\n \u9879\u76EE\u68C0\u6D4B\u7ED3\u679C:"));
|
|
2111
|
+
console.log(chalk3.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1999
2112
|
const items = [
|
|
2000
2113
|
["\u9879\u76EE\u540D\u79F0", info.name],
|
|
2001
2114
|
["\u6846\u67B6", info.frameworkConfidence > 0.5 ? `${info.frameworkDisplayName} (\u7F6E\u4FE1\u5EA6 ${Math.round(info.frameworkConfidence * 100)}%)` : info.frameworkDisplayName],
|
|
@@ -2017,8 +2130,8 @@ function displayProjectInfo(info) {
|
|
|
2017
2130
|
items.push(["\u7EC4\u4EF6\u76EE\u5F55", info.componentDirs.join(", ")]);
|
|
2018
2131
|
}
|
|
2019
2132
|
for (const [label, value] of items) {
|
|
2020
|
-
const displayValue = value === true ?
|
|
2021
|
-
console.log(` ${
|
|
2133
|
+
const displayValue = value === true ? chalk3.green("\u2713") : value === false ? chalk3.red("\u2717") : String(value);
|
|
2134
|
+
console.log(` ${chalk3.white(label.padEnd(12))} ${displayValue}`);
|
|
2022
2135
|
}
|
|
2023
2136
|
console.log();
|
|
2024
2137
|
}
|
|
@@ -2049,34 +2162,34 @@ function createTestDirectories(config) {
|
|
|
2049
2162
|
}
|
|
2050
2163
|
function displayResult(configPath, createdDirs, totalTestableFiles, projectInfo) {
|
|
2051
2164
|
const relativeConfig = path6.relative(process.cwd(), configPath);
|
|
2052
|
-
console.log(
|
|
2053
|
-
console.log(
|
|
2054
|
-
console.log(
|
|
2165
|
+
console.log(chalk3.green("\n \u2713 \u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210!\n"));
|
|
2166
|
+
console.log(chalk3.white(" \u5DF2\u751F\u6210\u914D\u7F6E:"));
|
|
2167
|
+
console.log(chalk3.gray(` ${relativeConfig}`));
|
|
2055
2168
|
console.log();
|
|
2056
2169
|
if (createdDirs.length > 0) {
|
|
2057
|
-
console.log(
|
|
2170
|
+
console.log(chalk3.white(" \u5DF2\u521B\u5EFA\u76EE\u5F55:"));
|
|
2058
2171
|
for (const dir of createdDirs) {
|
|
2059
|
-
console.log(
|
|
2172
|
+
console.log(chalk3.gray(` ${dir}/`));
|
|
2060
2173
|
}
|
|
2061
2174
|
console.log();
|
|
2062
2175
|
}
|
|
2063
|
-
console.log(
|
|
2176
|
+
console.log(chalk3.cyan(" \u4E0B\u4E00\u6B65:"));
|
|
2064
2177
|
if (totalTestableFiles > 0) {
|
|
2065
|
-
console.log(
|
|
2178
|
+
console.log(chalk3.gray(` 1. qat create \u9009\u62E9\u6587\u4EF6\u5E76\u751F\u6210\u6D4B\u8BD5\u7528\u4F8B (\u53D1\u73B0 ${totalTestableFiles} \u4E2A\u53EF\u6D4B\u8BD5\u6587\u4EF6)`));
|
|
2066
2179
|
} else {
|
|
2067
|
-
console.log(
|
|
2180
|
+
console.log(chalk3.gray(" 1. qat create \u521B\u5EFA\u6D4B\u8BD5\u7528\u4F8B"));
|
|
2068
2181
|
}
|
|
2069
|
-
console.log(
|
|
2070
|
-
console.log(
|
|
2182
|
+
console.log(chalk3.gray(" 2. qat run \u6267\u884C\u6D4B\u8BD5"));
|
|
2183
|
+
console.log(chalk3.gray(" 3. qat status \u67E5\u770B AI \u6A21\u578B\u72B6\u6001"));
|
|
2071
2184
|
if (projectInfo.testFrameworks.length === 0) {
|
|
2072
2185
|
console.log();
|
|
2073
|
-
console.log(
|
|
2186
|
+
console.log(chalk3.yellow(" \u26A0 \u672A\u68C0\u6D4B\u5230\u6D4B\u8BD5\u6846\u67B6\u4F9D\u8D56\uFF0C\u8FD0\u884C qat run \u65F6\u4F1A\u63D0\u793A\u5B89\u88C5"));
|
|
2074
2187
|
}
|
|
2075
2188
|
console.log();
|
|
2076
2189
|
}
|
|
2077
2190
|
|
|
2078
2191
|
// src/commands/create.ts
|
|
2079
|
-
import
|
|
2192
|
+
import chalk5 from "chalk";
|
|
2080
2193
|
import inquirer2 from "inquirer";
|
|
2081
2194
|
import ora3 from "ora";
|
|
2082
2195
|
import fs8 from "fs";
|
|
@@ -2537,7 +2650,7 @@ function toPascalCase(str) {
|
|
|
2537
2650
|
}
|
|
2538
2651
|
|
|
2539
2652
|
// src/services/test-reviewer.ts
|
|
2540
|
-
import
|
|
2653
|
+
import chalk4 from "chalk";
|
|
2541
2654
|
import ora2 from "ora";
|
|
2542
2655
|
var MAX_RETRIES = 3;
|
|
2543
2656
|
var REVIEW_THRESHOLD = 0.6;
|
|
@@ -2557,6 +2670,9 @@ async function generateWithReview(params) {
|
|
|
2557
2670
|
for (let i = 0; i < MAX_RETRIES; i++) {
|
|
2558
2671
|
attempts = i + 1;
|
|
2559
2672
|
onAttempt?.(attempts, MAX_RETRIES);
|
|
2673
|
+
if (process.env.QAT_DEBUG === "true") {
|
|
2674
|
+
console.error(chalk4.gray(`[DEBUG] \u2500\u2500\u2500 \u751F\u6210\u8005 AI \u7B2C${attempts}\u6B21\u5C1D\u8BD5 \u2500\u2500\u2500`));
|
|
2675
|
+
}
|
|
2560
2676
|
const generationPrompt = i === 0 ? void 0 : `\u4E0A\u6B21\u751F\u6210\u7684\u6D4B\u8BD5\u672A\u901A\u8FC7\u5BA1\u8BA1\uFF0C\u8BF7\u6839\u636E\u4EE5\u4E0B\u53CD\u9988\u91CD\u65B0\u751F\u6210\uFF1A
|
|
2561
2677
|
\u5BA1\u8BA1\u8BC4\u5206: ${(lastReview.score * 100).toFixed(0)}%
|
|
2562
2678
|
\u5BA1\u8BA1\u610F\u89C1: ${lastReview.feedback}
|
|
@@ -2569,16 +2685,17 @@ ${lastReview.suggestions.map((s) => `- ${s}`).join("\n")}
|
|
|
2569
2685
|
const generateResponse = await generatorProvider.generateTest({
|
|
2570
2686
|
type: testType,
|
|
2571
2687
|
target: targetPath,
|
|
2572
|
-
context
|
|
2573
|
-
|
|
2574
|
-
--- \u5BA1\u8BA1\u53CD\u9988 ---
|
|
2575
|
-
${generationPrompt}`,
|
|
2688
|
+
// 重试时:源码已压缩在分析摘要中,context只传审计反馈以减少token
|
|
2689
|
+
context: i === 0 ? sourceCode : generationPrompt,
|
|
2576
2690
|
framework: framework || void 0,
|
|
2577
2691
|
analysis
|
|
2578
2692
|
});
|
|
2579
2693
|
currentCode = generateResponse.code;
|
|
2580
2694
|
currentDescription = generateResponse.description;
|
|
2581
2695
|
currentConfidence = generateResponse.confidence;
|
|
2696
|
+
if (process.env.QAT_DEBUG === "true") {
|
|
2697
|
+
console.error(chalk4.gray(`[DEBUG] \u2500\u2500\u2500 \u5BA1\u8BA1\u5458 AI \u5BA1\u67E5 \u2500\u2500\u2500`));
|
|
2698
|
+
}
|
|
2582
2699
|
const reviewRequest = {
|
|
2583
2700
|
target: targetPath,
|
|
2584
2701
|
sourceCode,
|
|
@@ -2610,20 +2727,20 @@ function printReviewReport(report) {
|
|
|
2610
2727
|
const approved = report.filter((r) => r.approved);
|
|
2611
2728
|
const failed = report.filter((r) => !r.approved);
|
|
2612
2729
|
console.log();
|
|
2613
|
-
console.log(
|
|
2614
|
-
console.log(
|
|
2730
|
+
console.log(chalk4.cyan(" \u5BA1\u8BA1\u62A5\u544A:"));
|
|
2731
|
+
console.log(chalk4.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
2615
2732
|
for (const entry of approved) {
|
|
2616
|
-
console.log(` ${
|
|
2733
|
+
console.log(` ${chalk4.green("\u2713")} ${chalk4.gray(entry.target)} ${chalk4.green(`(${(entry.score * 100).toFixed(0)}%`)}${entry.attempts > 1 ? chalk4.yellow(` ${entry.attempts}\u6B21\u5C1D\u8BD5`) : ""})`);
|
|
2617
2734
|
}
|
|
2618
2735
|
for (const entry of failed) {
|
|
2619
|
-
console.log(` ${
|
|
2736
|
+
console.log(` ${chalk4.red("\u2717")} ${chalk4.gray(entry.target)} ${chalk4.red(`(${(entry.score * 100).toFixed(0)}%)`)}`);
|
|
2620
2737
|
if (entry.issues.length > 0) {
|
|
2621
2738
|
for (const issue of entry.issues.slice(0, 3)) {
|
|
2622
|
-
console.log(
|
|
2739
|
+
console.log(chalk4.gray(` - ${issue}`));
|
|
2623
2740
|
}
|
|
2624
2741
|
}
|
|
2625
2742
|
if (entry.feedback) {
|
|
2626
|
-
console.log(
|
|
2743
|
+
console.log(chalk4.gray(` \u53CD\u9988: ${entry.feedback}`));
|
|
2627
2744
|
}
|
|
2628
2745
|
}
|
|
2629
2746
|
console.log();
|
|
@@ -2651,7 +2768,7 @@ function registerCreateCommand(program2) {
|
|
|
2651
2768
|
try {
|
|
2652
2769
|
await executeCreate(options);
|
|
2653
2770
|
} catch (error) {
|
|
2654
|
-
console.error(
|
|
2771
|
+
console.error(chalk5.red(`
|
|
2655
2772
|
\u2717 ${error instanceof Error ? error.message : String(error)}
|
|
2656
2773
|
`));
|
|
2657
2774
|
process.exit(1);
|
|
@@ -2676,7 +2793,7 @@ async function executeCreate(options) {
|
|
|
2676
2793
|
name: "types",
|
|
2677
2794
|
message: "\u9009\u62E9\u6D4B\u8BD5\u7C7B\u578B (\u7A7A\u683C\u9009\u62E9/\u53D6\u6D88\uFF0C\u56DE\u8F66\u786E\u8BA4):",
|
|
2678
2795
|
choices: Object.entries(TEST_TYPE_LABELS).map(([value, label]) => ({
|
|
2679
|
-
name: `${label} - ${
|
|
2796
|
+
name: `${label} - ${chalk5.gray(TEST_TYPE_DESCRIPTIONS[value])}`,
|
|
2680
2797
|
value,
|
|
2681
2798
|
short: label,
|
|
2682
2799
|
checked: value === "unit" || value === "component"
|
|
@@ -2793,7 +2910,7 @@ async function executeCreate(options) {
|
|
|
2793
2910
|
createdFiles.push({ type: testType, filePath });
|
|
2794
2911
|
} catch (error) {
|
|
2795
2912
|
spinner.fail(`${TEST_TYPE_LABELS[testType]} - ${path8.basename(testTarget)} \u521B\u5EFA\u5931\u8D25`);
|
|
2796
|
-
console.log(
|
|
2913
|
+
console.log(chalk5.gray(` ${error instanceof Error ? error.message : String(error)}`));
|
|
2797
2914
|
}
|
|
2798
2915
|
}
|
|
2799
2916
|
}
|
|
@@ -2807,13 +2924,13 @@ async function selectTargets(types, projectInfo, srcDir) {
|
|
|
2807
2924
|
if (types.includes("unit")) {
|
|
2808
2925
|
const utils = discoverUtilityFiles(process.cwd(), srcDir);
|
|
2809
2926
|
for (const f of utils.slice(0, 30)) {
|
|
2810
|
-
allTargets.push({ name: `${
|
|
2927
|
+
allTargets.push({ name: `${chalk5.gray("[unit]")} ${f}`, value: f });
|
|
2811
2928
|
}
|
|
2812
2929
|
}
|
|
2813
2930
|
if (types.includes("component")) {
|
|
2814
2931
|
const components = discoverVueComponents(process.cwd(), srcDir);
|
|
2815
2932
|
for (const f of components.slice(0, 30)) {
|
|
2816
|
-
allTargets.push({ name: `${
|
|
2933
|
+
allTargets.push({ name: `${chalk5.gray("[comp]")} ${f}`, value: f });
|
|
2817
2934
|
}
|
|
2818
2935
|
}
|
|
2819
2936
|
if (allTargets.length === 0) {
|
|
@@ -2834,7 +2951,7 @@ async function selectTargets(types, projectInfo, srcDir) {
|
|
|
2834
2951
|
message: "\u9009\u62E9\u88AB\u6D4B\u76EE\u6807 (\u7A7A\u683C\u9009\u62E9/\u53D6\u6D88\uFF0C\u56DE\u8F66\u786E\u8BA4):",
|
|
2835
2952
|
choices: [
|
|
2836
2953
|
...allTargets,
|
|
2837
|
-
{ name:
|
|
2954
|
+
{ name: chalk5.gray("\u624B\u52A8\u8F93\u5165\u8DEF\u5F84"), value: "__manual__" }
|
|
2838
2955
|
],
|
|
2839
2956
|
validate: (input) => {
|
|
2840
2957
|
if (input.length === 0) return "\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u76EE\u6807";
|
|
@@ -2933,38 +3050,38 @@ function getTestOutputDir(type) {
|
|
|
2933
3050
|
}
|
|
2934
3051
|
function displayCreateResult(createdFiles, skippedCount, usedAI) {
|
|
2935
3052
|
if (createdFiles.length === 0 && skippedCount === 0) {
|
|
2936
|
-
console.log(
|
|
3053
|
+
console.log(chalk5.yellow("\n \u6CA1\u6709\u521B\u5EFA\u4EFB\u4F55\u6D4B\u8BD5\u6587\u4EF6\n"));
|
|
2937
3054
|
return;
|
|
2938
3055
|
}
|
|
2939
|
-
console.log(
|
|
3056
|
+
console.log(chalk5.green(`
|
|
2940
3057
|
\u2713 \u5DF2\u521B\u5EFA ${createdFiles.length} \u4E2A\u6D4B\u8BD5\u6587\u4EF6`));
|
|
2941
3058
|
if (skippedCount > 0) {
|
|
2942
|
-
console.log(
|
|
3059
|
+
console.log(chalk5.yellow(` \u26A0 \u8DF3\u8FC7 ${skippedCount} \u4E2A\u5DF2\u5B58\u5728\u7684\u6587\u4EF6`));
|
|
2943
3060
|
}
|
|
2944
3061
|
console.log();
|
|
2945
3062
|
for (const { type, filePath } of createdFiles) {
|
|
2946
3063
|
const relativePath = path8.relative(process.cwd(), filePath);
|
|
2947
|
-
console.log(
|
|
3064
|
+
console.log(chalk5.white(` ${chalk5.cyan(TEST_TYPE_LABELS[type])} ${chalk5.gray(relativePath)}`));
|
|
2948
3065
|
}
|
|
2949
3066
|
if (usedAI) {
|
|
2950
3067
|
console.log();
|
|
2951
|
-
console.log(
|
|
3068
|
+
console.log(chalk5.magenta(" \u751F\u6210\u65B9\u5F0F: AI \u8F85\u52A9"));
|
|
2952
3069
|
}
|
|
2953
3070
|
console.log();
|
|
2954
|
-
console.log(
|
|
3071
|
+
console.log(chalk5.gray(" \u8FD0\u884C\u6D4B\u8BD5:"));
|
|
2955
3072
|
const uniqueTypes = [...new Set(createdFiles.map((f) => f.type))];
|
|
2956
3073
|
if (uniqueTypes.length === 1) {
|
|
2957
|
-
console.log(
|
|
3074
|
+
console.log(chalk5.cyan(` qat run -t ${uniqueTypes[0]}`));
|
|
2958
3075
|
} else {
|
|
2959
|
-
console.log(
|
|
2960
|
-
console.log(
|
|
2961
|
-
console.log(
|
|
3076
|
+
console.log(chalk5.cyan(` qat run -t ${uniqueTypes.join(" -t ")}`));
|
|
3077
|
+
console.log(chalk5.gray(" # \u6216\u8FD0\u884C\u5168\u90E8"));
|
|
3078
|
+
console.log(chalk5.cyan(" qat run"));
|
|
2962
3079
|
}
|
|
2963
3080
|
console.log();
|
|
2964
3081
|
}
|
|
2965
3082
|
|
|
2966
3083
|
// src/commands/run.ts
|
|
2967
|
-
import
|
|
3084
|
+
import chalk6 from "chalk";
|
|
2968
3085
|
import inquirer3 from "inquirer";
|
|
2969
3086
|
import ora4 from "ora";
|
|
2970
3087
|
import fs11 from "fs";
|
|
@@ -4055,7 +4172,7 @@ function registerRunCommand(program2) {
|
|
|
4055
4172
|
try {
|
|
4056
4173
|
await executeRun(options);
|
|
4057
4174
|
} catch (error) {
|
|
4058
|
-
console.error(
|
|
4175
|
+
console.error(chalk6.red(`
|
|
4059
4176
|
\u2717 ${error instanceof Error ? error.message : String(error)}
|
|
4060
4177
|
`));
|
|
4061
4178
|
process.exit(1);
|
|
@@ -4075,15 +4192,15 @@ async function executeRun(options) {
|
|
|
4075
4192
|
}
|
|
4076
4193
|
let typesToRun = await determineTypesToRun(type, file, config);
|
|
4077
4194
|
if (typesToRun.length === 0) {
|
|
4078
|
-
console.log(
|
|
4195
|
+
console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\uFF08\u8BF7\u5728 qat.config.ts \u4E2D\u542F\u7528\uFF09\n"));
|
|
4079
4196
|
return;
|
|
4080
4197
|
}
|
|
4081
4198
|
const missingDeps = checkTestDependencies(typesToRun);
|
|
4082
4199
|
if (missingDeps.length > 0) {
|
|
4083
|
-
console.log(
|
|
4200
|
+
console.log(chalk6.yellow("\n \u26A0 \u4EE5\u4E0B\u6D4B\u8BD5\u6846\u67B6\u4F9D\u8D56\u672A\u5B89\u88C5:\n"));
|
|
4084
4201
|
for (const dep of missingDeps) {
|
|
4085
|
-
console.log(
|
|
4086
|
-
console.log(
|
|
4202
|
+
console.log(chalk6.white(` ${dep.runner} (${dep.pkg})`));
|
|
4203
|
+
console.log(chalk6.gray(` \u5B89\u88C5\u547D\u4EE4: ${chalk6.cyan(dep.installCmd)}`));
|
|
4087
4204
|
}
|
|
4088
4205
|
console.log();
|
|
4089
4206
|
const { action } = await inquirer3.prompt([
|
|
@@ -4100,13 +4217,13 @@ async function executeRun(options) {
|
|
|
4100
4217
|
}
|
|
4101
4218
|
]);
|
|
4102
4219
|
if (action === "cancel") {
|
|
4103
|
-
console.log(
|
|
4220
|
+
console.log(chalk6.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
|
|
4104
4221
|
return;
|
|
4105
4222
|
}
|
|
4106
4223
|
if (action === "install") {
|
|
4107
4224
|
const installed = await installTestDependencies(missingDeps);
|
|
4108
4225
|
if (!installed) {
|
|
4109
|
-
console.log(
|
|
4226
|
+
console.log(chalk6.yellow(" \u90E8\u5206\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25\uFF0C\u5C06\u8DF3\u8FC7\u5BF9\u5E94\u7684\u6D4B\u8BD5\u7C7B\u578B"));
|
|
4110
4227
|
}
|
|
4111
4228
|
const stillMissing = checkTestDependencies(typesToRun);
|
|
4112
4229
|
if (stillMissing.length > 0) {
|
|
@@ -4116,7 +4233,7 @@ async function executeRun(options) {
|
|
|
4116
4233
|
return !dep || !missingRunners.has(dep.pkg);
|
|
4117
4234
|
});
|
|
4118
4235
|
if (typesToRun.length === 0) {
|
|
4119
|
-
console.log(
|
|
4236
|
+
console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\uFF08\u4F9D\u8D56\u672A\u5B89\u88C5\uFF09\n"));
|
|
4120
4237
|
return;
|
|
4121
4238
|
}
|
|
4122
4239
|
}
|
|
@@ -4128,7 +4245,7 @@ async function executeRun(options) {
|
|
|
4128
4245
|
return !dep || !missingRunners.has(dep.pkg);
|
|
4129
4246
|
});
|
|
4130
4247
|
if (typesToRun.length === 0) {
|
|
4131
|
-
console.log(
|
|
4248
|
+
console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
|
|
4132
4249
|
return;
|
|
4133
4250
|
}
|
|
4134
4251
|
}
|
|
@@ -4151,13 +4268,13 @@ async function executeRun(options) {
|
|
|
4151
4268
|
}
|
|
4152
4269
|
]);
|
|
4153
4270
|
if (action === "cancel") {
|
|
4154
|
-
console.log(
|
|
4271
|
+
console.log(chalk6.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
|
|
4155
4272
|
return;
|
|
4156
4273
|
}
|
|
4157
4274
|
if (action === "skip") {
|
|
4158
4275
|
typesToRun = typesToRun.filter((t) => !SERVER_REQUIRED_TYPES.includes(t));
|
|
4159
4276
|
if (typesToRun.length === 0) {
|
|
4160
|
-
console.log(
|
|
4277
|
+
console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
|
|
4161
4278
|
return;
|
|
4162
4279
|
}
|
|
4163
4280
|
}
|
|
@@ -4177,12 +4294,12 @@ async function executeRun(options) {
|
|
|
4177
4294
|
}
|
|
4178
4295
|
]);
|
|
4179
4296
|
if (fallback === "cancel") {
|
|
4180
|
-
console.log(
|
|
4297
|
+
console.log(chalk6.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
|
|
4181
4298
|
return;
|
|
4182
4299
|
}
|
|
4183
4300
|
typesToRun = typesToRun.filter((t) => !SERVER_REQUIRED_TYPES.includes(t));
|
|
4184
4301
|
if (typesToRun.length === 0) {
|
|
4185
|
-
console.log(
|
|
4302
|
+
console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
|
|
4186
4303
|
return;
|
|
4187
4304
|
}
|
|
4188
4305
|
}
|
|
@@ -4240,8 +4357,8 @@ async function executeRun(options) {
|
|
|
4240
4357
|
const outputDir = config.report.outputDir || "qat-report";
|
|
4241
4358
|
const reportPath = writeReportToDisk(reportData, outputDir);
|
|
4242
4359
|
const relativePath = path12.relative(process.cwd(), reportPath);
|
|
4243
|
-
console.log(
|
|
4244
|
-
\u62A5\u544A\u5DF2\u751F\u6210: ${
|
|
4360
|
+
console.log(chalk6.gray(`
|
|
4361
|
+
\u62A5\u544A\u5DF2\u751F\u6210: ${chalk6.cyan(relativePath)}`));
|
|
4245
4362
|
console.log();
|
|
4246
4363
|
const hasFailures = results.some((r) => r.status === "failed");
|
|
4247
4364
|
if (hasFailures && isAIAvailable(config.ai)) {
|
|
@@ -4250,7 +4367,7 @@ async function executeRun(options) {
|
|
|
4250
4367
|
}
|
|
4251
4368
|
function printDryRunCommands(types, options, config) {
|
|
4252
4369
|
console.log();
|
|
4253
|
-
console.log(
|
|
4370
|
+
console.log(chalk6.cyan(" \u53EF\u6267\u884C\u7684\u6D4B\u8BD5\u547D\u4EE4:\n"));
|
|
4254
4371
|
for (const testType of types) {
|
|
4255
4372
|
const label = TYPE_LABELS2[testType];
|
|
4256
4373
|
const runner = TYPE_RUNNERS[testType];
|
|
@@ -4287,17 +4404,17 @@ function printDryRunCommands(types, options, config) {
|
|
|
4287
4404
|
default:
|
|
4288
4405
|
cmd = `# ${label}: \u672A\u77E5\u8FD0\u884C\u5668`;
|
|
4289
4406
|
}
|
|
4290
|
-
console.log(
|
|
4291
|
-
console.log(
|
|
4407
|
+
console.log(chalk6.white(` ${label}:`));
|
|
4408
|
+
console.log(chalk6.cyan(` ${cmd}`));
|
|
4292
4409
|
console.log();
|
|
4293
4410
|
}
|
|
4294
|
-
console.log(
|
|
4411
|
+
console.log(chalk6.gray(" \u6216\u4F7F\u7528 QAT \u7EDF\u4E00\u8FD0\u884C:"));
|
|
4295
4412
|
if (types.length === 1) {
|
|
4296
|
-
console.log(
|
|
4413
|
+
console.log(chalk6.cyan(` qat run -t ${types[0]}`));
|
|
4297
4414
|
} else {
|
|
4298
|
-
console.log(
|
|
4299
|
-
console.log(
|
|
4300
|
-
console.log(
|
|
4415
|
+
console.log(chalk6.cyan(` qat run -t ${types.join(" -t ")}`));
|
|
4416
|
+
console.log(chalk6.gray(" # \u5E76\u884C\u8FD0\u884C"));
|
|
4417
|
+
console.log(chalk6.cyan(" qat run -p"));
|
|
4301
4418
|
}
|
|
4302
4419
|
console.log();
|
|
4303
4420
|
}
|
|
@@ -4320,7 +4437,7 @@ async function determineTypesToRun(type, file, config) {
|
|
|
4320
4437
|
name: "selectedTypes",
|
|
4321
4438
|
message: "\u9009\u62E9\u8981\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B (\u7A7A\u683C\u9009\u62E9/\u53D6\u6D88\uFF0C\u56DE\u8F66\u786E\u8BA4):",
|
|
4322
4439
|
choices: enabledTypes.map((t) => ({
|
|
4323
|
-
name: `${TYPE_LABELS2[t]} (${
|
|
4440
|
+
name: `${TYPE_LABELS2[t]} (${chalk6.gray(TYPE_RUNNERS[t])})`,
|
|
4324
4441
|
value: t,
|
|
4325
4442
|
checked: true
|
|
4326
4443
|
})),
|
|
@@ -4380,7 +4497,7 @@ async function runTestType(testType, config, options) {
|
|
|
4380
4497
|
}
|
|
4381
4498
|
}
|
|
4382
4499
|
async function runWatchMode(_config, options) {
|
|
4383
|
-
console.log(
|
|
4500
|
+
console.log(chalk6.cyan(" \u76D1\u542C\u6A21\u5F0F\u5DF2\u542F\u52A8 (Ctrl+C \u9000\u51FA)\n"));
|
|
4384
4501
|
const { spawn } = await import("child_process");
|
|
4385
4502
|
const args = ["vitest", "--watch"];
|
|
4386
4503
|
if (options.file) {
|
|
@@ -4404,7 +4521,7 @@ async function runWatchMode(_config, options) {
|
|
|
4404
4521
|
shell: true
|
|
4405
4522
|
});
|
|
4406
4523
|
child.on("error", (err) => {
|
|
4407
|
-
console.error(
|
|
4524
|
+
console.error(chalk6.red(`
|
|
4408
4525
|
Vitest \u542F\u52A8\u5931\u8D25: ${err.message}
|
|
4409
4526
|
`));
|
|
4410
4527
|
});
|
|
@@ -4459,17 +4576,17 @@ function displayJestStyleResults(results) {
|
|
|
4459
4576
|
for (const result of results) {
|
|
4460
4577
|
const typeLabel = TYPE_LABELS2[result.type] || result.type;
|
|
4461
4578
|
if (result.suites.length === 0) {
|
|
4462
|
-
console.log(
|
|
4579
|
+
console.log(chalk6.gray(` ${typeLabel}: \u65E0\u6D4B\u8BD5\u7ED3\u679C`));
|
|
4463
4580
|
continue;
|
|
4464
4581
|
}
|
|
4465
4582
|
for (const suite of result.suites) {
|
|
4466
4583
|
if (suite.tests.length === 0) continue;
|
|
4467
|
-
const suiteIcon = suite.status === "passed" ?
|
|
4468
|
-
console.log(` ${suiteIcon} ${
|
|
4584
|
+
const suiteIcon = suite.status === "passed" ? chalk6.green("PASS") : chalk6.red("FAIL");
|
|
4585
|
+
console.log(` ${suiteIcon} ${chalk6.white(suite.name)} ${chalk6.gray(`(${formatDuration2(suite.duration)})`)}`);
|
|
4469
4586
|
for (const test of suite.tests) {
|
|
4470
|
-
const icon = test.status === "passed" ?
|
|
4471
|
-
const name = test.status === "failed" ?
|
|
4472
|
-
console.log(` ${icon} ${name} ${
|
|
4587
|
+
const icon = test.status === "passed" ? chalk6.green(" \u2713") : test.status === "failed" ? chalk6.red(" \u2715") : chalk6.yellow(" \u25CB");
|
|
4588
|
+
const name = test.status === "failed" ? chalk6.red(test.name) : test.name;
|
|
4589
|
+
console.log(` ${icon} ${name} ${chalk6.gray(formatDuration2(test.duration))}`);
|
|
4473
4590
|
}
|
|
4474
4591
|
}
|
|
4475
4592
|
console.log();
|
|
@@ -4501,28 +4618,28 @@ function displayJestStyleResults(results) {
|
|
|
4501
4618
|
}
|
|
4502
4619
|
}
|
|
4503
4620
|
const total = totalPassed + totalFailed + totalSkipped;
|
|
4504
|
-
console.log(
|
|
4505
|
-
console.log(
|
|
4506
|
-
console.log(
|
|
4621
|
+
console.log(chalk6.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
4622
|
+
console.log(chalk6.white(" \u6D4B\u8BD5\u7ED3\u679C\u6C47\u603B"));
|
|
4623
|
+
console.log(chalk6.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
4507
4624
|
console.log();
|
|
4508
|
-
console.log(` ${
|
|
4625
|
+
console.log(` ${chalk6.white("\u7C7B\u578B".padEnd(14))} ${chalk6.white("\u901A\u8FC7".padStart(6))} ${chalk6.white("\u5931\u8D25".padStart(6))} ${chalk6.white("\u8DF3\u8FC7".padStart(6))} ${chalk6.white("\u603B\u8BA1".padStart(6))} ${chalk6.white("\u901A\u8FC7\u7387".padStart(8))} ${chalk6.white("\u8017\u65F6".padStart(8))}`);
|
|
4509
4626
|
console.log(` ${"\u2500".repeat(14)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(8)} ${"\u2500".repeat(8)}`);
|
|
4510
4627
|
for (const [type, stats] of Object.entries(typeStats)) {
|
|
4511
4628
|
const label = (TYPE_LABELS2[type] || type).padEnd(14);
|
|
4512
4629
|
const typeTotal = stats.passed + stats.failed + stats.skipped;
|
|
4513
4630
|
const rate = typeTotal > 0 ? (stats.passed / typeTotal * 100).toFixed(0) + "%" : "-";
|
|
4514
|
-
const rateColored = typeTotal > 0 && stats.passed === typeTotal ?
|
|
4631
|
+
const rateColored = typeTotal > 0 && stats.passed === typeTotal ? chalk6.green(rate.padStart(8)) : stats.failed > 0 ? chalk6.red(rate.padStart(8)) : rate.padStart(8);
|
|
4515
4632
|
console.log(` ${label} ${String(stats.passed).padStart(6)} ${String(stats.failed).padStart(6)} ${String(stats.skipped).padStart(6)} ${String(typeTotal).padStart(6)} ${rateColored} ${formatDuration2(stats.duration).padStart(8)}`);
|
|
4516
4633
|
}
|
|
4517
4634
|
console.log(` ${"\u2500".repeat(14)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(8)} ${"\u2500".repeat(8)}`);
|
|
4518
4635
|
const totalRate = total > 0 ? (totalPassed / total * 100).toFixed(0) + "%" : "-";
|
|
4519
|
-
const totalRateColored = total > 0 && totalFailed === 0 ?
|
|
4636
|
+
const totalRateColored = total > 0 && totalFailed === 0 ? chalk6.green(totalRate.padStart(8)) : totalFailed > 0 ? chalk6.red(totalRate.padStart(8)) : totalRate.padStart(8);
|
|
4520
4637
|
console.log(` ${"\u603B\u8BA1".padEnd(14)} ${String(totalPassed).padStart(6)} ${String(totalFailed).padStart(6)} ${String(totalSkipped).padStart(6)} ${String(total).padStart(6)} ${totalRateColored} ${formatDuration2(totalDuration).padStart(8)}`);
|
|
4521
4638
|
console.log();
|
|
4522
4639
|
for (const result of results) {
|
|
4523
4640
|
if (result.coverage) {
|
|
4524
4641
|
const c = result.coverage;
|
|
4525
|
-
console.log(
|
|
4642
|
+
console.log(chalk6.cyan(" \u8986\u76D6\u7387:"));
|
|
4526
4643
|
console.log(` \u8BED\u53E5: ${coverageColor(c.statements)} \u5206\u652F: ${coverageColor(c.branches)} \u51FD\u6570: ${coverageColor(c.functions)} \u884C: ${coverageColor(c.lines)}`);
|
|
4527
4644
|
console.log();
|
|
4528
4645
|
}
|
|
@@ -4530,7 +4647,7 @@ function displayJestStyleResults(results) {
|
|
|
4530
4647
|
for (const result of results) {
|
|
4531
4648
|
if (result.type === "performance" && result.performance) {
|
|
4532
4649
|
const p = result.performance;
|
|
4533
|
-
console.log(
|
|
4650
|
+
console.log(chalk6.cyan(" \u6027\u80FD\u6307\u6807:"));
|
|
4534
4651
|
console.log(` Performance: ${scoreColor(p.performance)} ${p.performance}/100`);
|
|
4535
4652
|
console.log(` Accessibility: ${scoreColor(p.accessibility)} ${p.accessibility}/100`);
|
|
4536
4653
|
console.log(` Best Practices: ${scoreColor(p.bestPractices)} ${p.bestPractices}/100`);
|
|
@@ -4547,31 +4664,31 @@ function displayJestStyleResults(results) {
|
|
|
4547
4664
|
)
|
|
4548
4665
|
);
|
|
4549
4666
|
if (failedTests.length > 0) {
|
|
4550
|
-
console.log(
|
|
4667
|
+
console.log(chalk6.red(" \u5931\u8D25\u8BE6\u60C5:"));
|
|
4551
4668
|
for (const { suite, test } of failedTests) {
|
|
4552
|
-
console.log(
|
|
4669
|
+
console.log(chalk6.red(` \u2715 ${suite.name} > ${test.name}`));
|
|
4553
4670
|
if (test.error?.message) {
|
|
4554
|
-
console.log(
|
|
4671
|
+
console.log(chalk6.gray(` ${test.error.message.split("\n")[0]}`));
|
|
4555
4672
|
}
|
|
4556
4673
|
}
|
|
4557
4674
|
console.log();
|
|
4558
4675
|
}
|
|
4559
4676
|
if (totalFailed > 0) {
|
|
4560
|
-
console.log(
|
|
4677
|
+
console.log(chalk6.red(` Tests: ${totalFailed} failed, ${totalPassed} passed, ${total} total`));
|
|
4561
4678
|
process.exitCode = 1;
|
|
4562
4679
|
} else if (total === 0) {
|
|
4563
|
-
console.log(
|
|
4680
|
+
console.log(chalk6.yellow(" \u6CA1\u6709\u53D1\u73B0\u6D4B\u8BD5\u7528\u4F8B"));
|
|
4564
4681
|
} else {
|
|
4565
|
-
console.log(
|
|
4682
|
+
console.log(chalk6.green(` Tests: ${totalPassed} passed, ${total} total`));
|
|
4566
4683
|
}
|
|
4567
|
-
console.log(
|
|
4684
|
+
console.log(chalk6.gray(` Time: ${formatDuration2(totalDuration)}`));
|
|
4568
4685
|
console.log();
|
|
4569
4686
|
}
|
|
4570
4687
|
function coverageColor(value) {
|
|
4571
4688
|
const pct3 = `${(value * 100).toFixed(1)}%`;
|
|
4572
|
-
if (value >= 0.8) return
|
|
4573
|
-
if (value >= 0.5) return
|
|
4574
|
-
return
|
|
4689
|
+
if (value >= 0.8) return chalk6.green(pct3);
|
|
4690
|
+
if (value >= 0.5) return chalk6.yellow(pct3);
|
|
4691
|
+
return chalk6.red(pct3);
|
|
4575
4692
|
}
|
|
4576
4693
|
async function aiAnalyzeFailures(results, aiConfig) {
|
|
4577
4694
|
const failedTests = results.flatMap(
|
|
@@ -4580,23 +4697,23 @@ async function aiAnalyzeFailures(results, aiConfig) {
|
|
|
4580
4697
|
)
|
|
4581
4698
|
);
|
|
4582
4699
|
if (failedTests.length === 0) return;
|
|
4583
|
-
console.log(
|
|
4700
|
+
console.log(chalk6.magenta(" \u{1F916} AI \u5206\u6790\u5931\u8D25\u539F\u56E0..."));
|
|
4584
4701
|
console.log();
|
|
4585
4702
|
const provider = getAIProvider(aiConfig);
|
|
4586
4703
|
if (!provider.capabilities.suggestFix) return;
|
|
4587
4704
|
for (const { suite, test } of failedTests.slice(0, 5)) {
|
|
4588
4705
|
try {
|
|
4589
4706
|
const suggestions = await provider.suggestFix(test.error);
|
|
4590
|
-
console.log(
|
|
4707
|
+
console.log(chalk6.white(` ${suite.name} > ${test.name}`));
|
|
4591
4708
|
if (test.error?.message) {
|
|
4592
|
-
console.log(
|
|
4709
|
+
console.log(chalk6.gray(` \u9519\u8BEF: ${test.error.message.split("\n")[0]}`));
|
|
4593
4710
|
}
|
|
4594
4711
|
for (const suggestion of suggestions.slice(0, 3)) {
|
|
4595
|
-
console.log(
|
|
4712
|
+
console.log(chalk6.cyan(` \u2192 ${suggestion}`));
|
|
4596
4713
|
}
|
|
4597
4714
|
console.log();
|
|
4598
4715
|
} catch (error) {
|
|
4599
|
-
console.log(
|
|
4716
|
+
console.log(chalk6.gray(` AI \u5206\u6790\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`));
|
|
4600
4717
|
}
|
|
4601
4718
|
}
|
|
4602
4719
|
}
|
|
@@ -4608,9 +4725,9 @@ function formatDuration2(ms) {
|
|
|
4608
4725
|
return `${min}m ${sec}s`;
|
|
4609
4726
|
}
|
|
4610
4727
|
function scoreColor(score) {
|
|
4611
|
-
if (score >= 90) return
|
|
4612
|
-
if (score >= 50) return
|
|
4613
|
-
return
|
|
4728
|
+
if (score >= 90) return chalk6.green("\u25CF");
|
|
4729
|
+
if (score >= 50) return chalk6.yellow("\u25CF");
|
|
4730
|
+
return chalk6.red("\u25CF");
|
|
4614
4731
|
}
|
|
4615
4732
|
async function checkDevServer(config) {
|
|
4616
4733
|
const baseUrl = config.playwright.baseURL || "http://localhost:5173";
|
|
@@ -4629,7 +4746,7 @@ async function startDevServer(config) {
|
|
|
4629
4746
|
const hasPnpm = fs11.existsSync(path12.join(process.cwd(), "pnpm-lock.yaml"));
|
|
4630
4747
|
const hasYarn = fs11.existsSync(path12.join(process.cwd(), "yarn.lock"));
|
|
4631
4748
|
const pkgCmd = hasPnpm ? "pnpm" : hasYarn ? "yarn" : "npm";
|
|
4632
|
-
console.log(
|
|
4749
|
+
console.log(chalk6.cyan(` \u6B63\u5728\u542F\u52A8 dev server (${pkgCmd} run dev) ...`));
|
|
4633
4750
|
const child = spawn(pkgCmd, ["run", "dev"], {
|
|
4634
4751
|
cwd: process.cwd(),
|
|
4635
4752
|
stdio: "pipe",
|
|
@@ -4645,13 +4762,13 @@ async function startDevServer(config) {
|
|
|
4645
4762
|
waited += interval;
|
|
4646
4763
|
const isUp = await checkDevServer(config);
|
|
4647
4764
|
if (isUp) {
|
|
4648
|
-
console.log(
|
|
4765
|
+
console.log(chalk6.green(` \u2713 dev server \u5DF2\u542F\u52A8 (${baseUrl})`));
|
|
4649
4766
|
child.unref();
|
|
4650
4767
|
return true;
|
|
4651
4768
|
}
|
|
4652
4769
|
}
|
|
4653
4770
|
child.kill();
|
|
4654
|
-
console.log(
|
|
4771
|
+
console.log(chalk6.red(" \u2717 dev server \u542F\u52A8\u8D85\u65F6"));
|
|
4655
4772
|
return false;
|
|
4656
4773
|
}
|
|
4657
4774
|
function saveRunResults(results) {
|
|
@@ -4743,7 +4860,7 @@ async function installTestDependencies(missingDeps) {
|
|
|
4743
4860
|
spinner.succeed(`${dep.pkg} \u5B89\u88C5\u6210\u529F`);
|
|
4744
4861
|
} catch (error) {
|
|
4745
4862
|
spinner.fail(`${dep.pkg} \u5B89\u88C5\u5931\u8D25`);
|
|
4746
|
-
console.log(
|
|
4863
|
+
console.log(chalk6.gray(` \u53EF\u624B\u52A8\u5B89\u88C5: ${chalk6.cyan(dep.installCmd)}`));
|
|
4747
4864
|
allSuccess = false;
|
|
4748
4865
|
}
|
|
4749
4866
|
}
|
|
@@ -4751,7 +4868,7 @@ async function installTestDependencies(missingDeps) {
|
|
|
4751
4868
|
}
|
|
4752
4869
|
|
|
4753
4870
|
// src/commands/mock.ts
|
|
4754
|
-
import
|
|
4871
|
+
import chalk7 from "chalk";
|
|
4755
4872
|
import ora5 from "ora";
|
|
4756
4873
|
function registerMockCommand(program2) {
|
|
4757
4874
|
program2.command("mock").description("Mock\u670D\u52A1\u7BA1\u7406 - \u542F\u52A8/\u505C\u6B62Mock API\u670D\u52A1\u5668").argument("<action>", "\u64CD\u4F5C\u7C7B\u578B (start|stop|status)").option("-p, --port <port>", "\u6307\u5B9A\u7AEF\u53E3\u53F7", "3456").action(async (action, options) => {
|
|
@@ -4762,7 +4879,7 @@ function registerMockCommand(program2) {
|
|
|
4762
4879
|
config: options.config
|
|
4763
4880
|
});
|
|
4764
4881
|
} catch (error) {
|
|
4765
|
-
console.error(
|
|
4882
|
+
console.error(chalk7.red(`
|
|
4766
4883
|
\u2717 ${error instanceof Error ? error.message : String(error)}
|
|
4767
4884
|
`));
|
|
4768
4885
|
process.exit(1);
|
|
@@ -4786,9 +4903,9 @@ async function executeMock(options) {
|
|
|
4786
4903
|
break;
|
|
4787
4904
|
}
|
|
4788
4905
|
default: {
|
|
4789
|
-
console.error(
|
|
4906
|
+
console.error(chalk7.red(`
|
|
4790
4907
|
\u672A\u77E5\u64CD\u4F5C: ${options.action}`));
|
|
4791
|
-
console.log(
|
|
4908
|
+
console.log(chalk7.gray(" \u53EF\u7528\u64CD\u4F5C: start, stop, status\n"));
|
|
4792
4909
|
process.exit(1);
|
|
4793
4910
|
}
|
|
4794
4911
|
}
|
|
@@ -4796,7 +4913,7 @@ async function executeMock(options) {
|
|
|
4796
4913
|
async function startMock(port, routesDir) {
|
|
4797
4914
|
const state = getMockServerState();
|
|
4798
4915
|
if (state.running) {
|
|
4799
|
-
console.log(
|
|
4916
|
+
console.log(chalk7.yellow(`
|
|
4800
4917
|
Mock\u670D\u52A1\u5668\u5DF2\u5728\u8FD0\u884C (\u7AEF\u53E3: ${state.port})
|
|
4801
4918
|
`));
|
|
4802
4919
|
return;
|
|
@@ -4810,22 +4927,22 @@ async function startMock(port, routesDir) {
|
|
|
4810
4927
|
await startMockServer(port, routes);
|
|
4811
4928
|
spinner.succeed(`Mock\u670D\u52A1\u5668\u5DF2\u542F\u52A8`);
|
|
4812
4929
|
console.log();
|
|
4813
|
-
console.log(
|
|
4814
|
-
console.log(
|
|
4815
|
-
console.log(
|
|
4930
|
+
console.log(chalk7.white(" \u5730\u5740:"), chalk7.cyan(`http://localhost:${port}`));
|
|
4931
|
+
console.log(chalk7.white(" \u8DEF\u7531:"), routes.length > 0 ? `${routes.length} \u4E2A` : chalk7.gray("\u4F7F\u7528\u9ED8\u8BA4\u8DEF\u7531"));
|
|
4932
|
+
console.log(chalk7.white(" \u5065\u5EB7\u68C0\u67E5:"), chalk7.cyan(`http://localhost:${port}/api/health`));
|
|
4816
4933
|
if (routes.length > 0) {
|
|
4817
4934
|
console.log();
|
|
4818
|
-
console.log(
|
|
4935
|
+
console.log(chalk7.white(" \u5DF2\u6CE8\u518C\u8DEF\u7531:"));
|
|
4819
4936
|
for (const route of routes.slice(0, 10)) {
|
|
4820
|
-
const method =
|
|
4937
|
+
const method = chalk7.gray(route.method.padEnd(6));
|
|
4821
4938
|
console.log(` ${method} ${route.path}`);
|
|
4822
4939
|
}
|
|
4823
4940
|
if (routes.length > 10) {
|
|
4824
|
-
console.log(
|
|
4941
|
+
console.log(chalk7.gray(` ... \u8FD8\u6709 ${routes.length - 10} \u4E2A\u8DEF\u7531`));
|
|
4825
4942
|
}
|
|
4826
4943
|
}
|
|
4827
4944
|
console.log();
|
|
4828
|
-
console.log(
|
|
4945
|
+
console.log(chalk7.gray(" \u6309 Ctrl+C \u505C\u6B62\u670D\u52A1\u5668"));
|
|
4829
4946
|
process.on("SIGINT", async () => {
|
|
4830
4947
|
const stopSpinner = ora5("\u6B63\u5728\u505C\u6B62Mock\u670D\u52A1\u5668...").start();
|
|
4831
4948
|
await stopMockServer();
|
|
@@ -4842,7 +4959,7 @@ async function startMock(port, routesDir) {
|
|
|
4842
4959
|
async function stopMock() {
|
|
4843
4960
|
const state = getMockServerState();
|
|
4844
4961
|
if (!state.running) {
|
|
4845
|
-
console.log(
|
|
4962
|
+
console.log(chalk7.yellow("\n Mock\u670D\u52A1\u5668\u672A\u5728\u8FD0\u884C\n"));
|
|
4846
4963
|
return;
|
|
4847
4964
|
}
|
|
4848
4965
|
const spinner = ora5("\u6B63\u5728\u505C\u6B62Mock\u670D\u52A1\u5668...").start();
|
|
@@ -4859,18 +4976,18 @@ function showStatus() {
|
|
|
4859
4976
|
const state = getMockServerState();
|
|
4860
4977
|
console.log();
|
|
4861
4978
|
if (state.running) {
|
|
4862
|
-
console.log(
|
|
4863
|
-
console.log(
|
|
4864
|
-
console.log(
|
|
4979
|
+
console.log(chalk7.green(" \u25CF Mock\u670D\u52A1\u5668\u8FD0\u884C\u4E2D"));
|
|
4980
|
+
console.log(chalk7.white(" \u7AEF\u53E3:"), state.port);
|
|
4981
|
+
console.log(chalk7.white(" \u8DEF\u7531:"), state.routes.length);
|
|
4865
4982
|
} else {
|
|
4866
|
-
console.log(
|
|
4867
|
-
console.log(
|
|
4983
|
+
console.log(chalk7.gray(" \u25CB Mock\u670D\u52A1\u5668\u672A\u8FD0\u884C"));
|
|
4984
|
+
console.log(chalk7.gray(" \u4F7F\u7528 qat mock start \u542F\u52A8"));
|
|
4868
4985
|
}
|
|
4869
4986
|
console.log();
|
|
4870
4987
|
}
|
|
4871
4988
|
|
|
4872
4989
|
// src/commands/report.ts
|
|
4873
|
-
import
|
|
4990
|
+
import chalk8 from "chalk";
|
|
4874
4991
|
import ora6 from "ora";
|
|
4875
4992
|
import fs12 from "fs";
|
|
4876
4993
|
import path13 from "path";
|
|
@@ -4880,7 +4997,7 @@ function registerReportCommand(program2) {
|
|
|
4880
4997
|
try {
|
|
4881
4998
|
await executeReport(options);
|
|
4882
4999
|
} catch (error) {
|
|
4883
|
-
console.error(
|
|
5000
|
+
console.error(chalk8.red(`
|
|
4884
5001
|
\u2717 ${error instanceof Error ? error.message : String(error)}
|
|
4885
5002
|
`));
|
|
4886
5003
|
process.exit(1);
|
|
@@ -4894,7 +5011,7 @@ async function executeReport(options) {
|
|
|
4894
5011
|
const results = collectResults();
|
|
4895
5012
|
if (results.length === 0) {
|
|
4896
5013
|
spinner.info("\u6CA1\u6709\u627E\u5230\u6D4B\u8BD5\u7ED3\u679C");
|
|
4897
|
-
console.log(
|
|
5014
|
+
console.log(chalk8.gray("\n \u63D0\u793A: \u5148\u8FD0\u884C qat run \u751F\u6210\u6D4B\u8BD5\u7ED3\u679C\n"));
|
|
4898
5015
|
return;
|
|
4899
5016
|
}
|
|
4900
5017
|
spinner.text = "\u6B63\u5728\u751F\u6210\u6D4B\u8BD5\u62A5\u544A...";
|
|
@@ -4947,31 +5064,31 @@ function displayReportResult(reportPath, data) {
|
|
|
4947
5064
|
const relativePath = path13.relative(process.cwd(), reportPath);
|
|
4948
5065
|
const passRate = data.summary.total > 0 ? (data.summary.passed / data.summary.total * 100).toFixed(1) : "0";
|
|
4949
5066
|
console.log();
|
|
4950
|
-
console.log(
|
|
5067
|
+
console.log(chalk8.green(" \u2713 \u6D4B\u8BD5\u62A5\u544A\u5DF2\u751F\u6210"));
|
|
4951
5068
|
console.log();
|
|
4952
|
-
console.log(
|
|
4953
|
-
console.log(
|
|
4954
|
-
console.log(
|
|
4955
|
-
console.log(
|
|
5069
|
+
console.log(chalk8.white(" \u62A5\u544A\u8DEF\u5F84:"), chalk8.cyan(relativePath));
|
|
5070
|
+
console.log(chalk8.white(" \u901A\u8FC7\u7387: "), parseFloat(passRate) >= 80 ? chalk8.green(`${passRate}%`) : parseFloat(passRate) >= 50 ? chalk8.yellow(`${passRate}%`) : chalk8.red(`${passRate}%`));
|
|
5071
|
+
console.log(chalk8.white(" \u6D4B\u8BD5\u7528\u4F8B:"), `${data.summary.total} total`);
|
|
5072
|
+
console.log(chalk8.white(" \u2705 \u901A\u8FC7: "), chalk8.green(String(data.summary.passed)));
|
|
4956
5073
|
if (data.summary.failed > 0) {
|
|
4957
|
-
console.log(
|
|
5074
|
+
console.log(chalk8.white(" \u274C \u5931\u8D25: "), chalk8.red(String(data.summary.failed)));
|
|
4958
5075
|
}
|
|
4959
5076
|
if (data.summary.skipped > 0) {
|
|
4960
|
-
console.log(
|
|
5077
|
+
console.log(chalk8.white(" \u23ED\uFE0F \u8DF3\u8FC7: "), chalk8.yellow(String(data.summary.skipped)));
|
|
4961
5078
|
}
|
|
4962
5079
|
if (Object.keys(data.byType).length > 0) {
|
|
4963
5080
|
console.log();
|
|
4964
|
-
console.log(
|
|
5081
|
+
console.log(chalk8.white(" \u6309\u7C7B\u578B:"));
|
|
4965
5082
|
for (const [type, stats] of Object.entries(data.byType)) {
|
|
4966
5083
|
const rate = stats.total > 0 ? (stats.passed / stats.total * 100).toFixed(0) : "0";
|
|
4967
|
-
const icon = stats.failed > 0 ?
|
|
5084
|
+
const icon = stats.failed > 0 ? chalk8.red("\u274C") : chalk8.green("\u2705");
|
|
4968
5085
|
console.log(` ${icon} ${type}: ${stats.passed}/${stats.total} (${rate}%)`);
|
|
4969
5086
|
}
|
|
4970
5087
|
}
|
|
4971
5088
|
if (data.coverage) {
|
|
4972
5089
|
console.log();
|
|
4973
|
-
console.log(
|
|
4974
|
-
console.log(` \u8BED\u53E5: ${
|
|
5090
|
+
console.log(chalk8.white(" \u8986\u76D6\u7387:"));
|
|
5091
|
+
console.log(` \u8BED\u53E5: ${chalk8.cyan(pct2(data.coverage.statements))} \u5206\u652F: ${chalk8.cyan(pct2(data.coverage.branches))} \u51FD\u6570: ${chalk8.cyan(pct2(data.coverage.functions))} \u884C: ${chalk8.cyan(pct2(data.coverage.lines))}`);
|
|
4975
5092
|
}
|
|
4976
5093
|
console.log();
|
|
4977
5094
|
}
|
|
@@ -4991,13 +5108,13 @@ async function openReport(reportPath) {
|
|
|
4991
5108
|
}
|
|
4992
5109
|
exec(command, { shell: true }, (error) => {
|
|
4993
5110
|
if (error) {
|
|
4994
|
-
console.log(
|
|
5111
|
+
console.log(chalk8.gray(" \u63D0\u793A: \u624B\u52A8\u6253\u5F00\u62A5\u544A\u67E5\u770B"));
|
|
4995
5112
|
}
|
|
4996
5113
|
});
|
|
4997
5114
|
}
|
|
4998
5115
|
|
|
4999
5116
|
// src/commands/visual.ts
|
|
5000
|
-
import
|
|
5117
|
+
import chalk9 from "chalk";
|
|
5001
5118
|
import ora7 from "ora";
|
|
5002
5119
|
|
|
5003
5120
|
// src/services/visual.ts
|
|
@@ -5164,7 +5281,7 @@ function registerVisualCommand(program2) {
|
|
|
5164
5281
|
config: options.config
|
|
5165
5282
|
});
|
|
5166
5283
|
} catch (error) {
|
|
5167
|
-
console.error(
|
|
5284
|
+
console.error(chalk9.red(`
|
|
5168
5285
|
\u2717 ${error instanceof Error ? error.message : String(error)}
|
|
5169
5286
|
`));
|
|
5170
5287
|
process.exit(1);
|
|
@@ -5190,9 +5307,9 @@ async function executeVisual(options) {
|
|
|
5190
5307
|
break;
|
|
5191
5308
|
}
|
|
5192
5309
|
default: {
|
|
5193
|
-
console.error(
|
|
5310
|
+
console.error(chalk9.red(`
|
|
5194
5311
|
\u672A\u77E5\u64CD\u4F5C: ${options.action}`));
|
|
5195
|
-
console.log(
|
|
5312
|
+
console.log(chalk9.gray(" \u53EF\u7528\u64CD\u4F5C: test, approve, clean\n"));
|
|
5196
5313
|
process.exit(1);
|
|
5197
5314
|
}
|
|
5198
5315
|
}
|
|
@@ -5216,7 +5333,7 @@ async function runVisualTest(threshold, baselineDir, diffDir, config) {
|
|
|
5216
5333
|
const currentDir = findCurrentScreenshotsDir(baselineDir);
|
|
5217
5334
|
if (!currentDir) {
|
|
5218
5335
|
compareSpinner.info("\u6CA1\u6709\u627E\u5230\u622A\u56FE\u6587\u4EF6");
|
|
5219
|
-
console.log(
|
|
5336
|
+
console.log(chalk9.gray("\n \u63D0\u793A: \u5148\u8FD0\u884C qat run -t visual \u751F\u6210\u622A\u56FE\n"));
|
|
5220
5337
|
return;
|
|
5221
5338
|
}
|
|
5222
5339
|
const results = compareDirectories(baselineDir, currentDir, diffDir, threshold);
|
|
@@ -5262,48 +5379,48 @@ function displayVisualResults(results, threshold) {
|
|
|
5262
5379
|
const passed = results.filter((r) => r.passed);
|
|
5263
5380
|
const failed = results.filter((r) => !r.passed);
|
|
5264
5381
|
console.log();
|
|
5265
|
-
console.log(
|
|
5382
|
+
console.log(chalk9.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5266
5383
|
if (failed.length === 0) {
|
|
5267
|
-
console.log(
|
|
5384
|
+
console.log(chalk9.green(` \u2713 \u5168\u90E8\u901A\u8FC7 (${results.length} \u4E2A\u622A\u56FE\u6BD4\u5BF9)`));
|
|
5268
5385
|
} else {
|
|
5269
|
-
console.log(
|
|
5386
|
+
console.log(chalk9.red(` \u2717 ${failed.length} \u4E2A\u622A\u56FE\u5B58\u5728\u5DEE\u5F02`));
|
|
5270
5387
|
}
|
|
5271
5388
|
console.log();
|
|
5272
|
-
console.log(` ${
|
|
5273
|
-
console.log(` ${
|
|
5274
|
-
console.log(` ${
|
|
5389
|
+
console.log(` ${chalk9.white("\u9608\u503C:")} ${(threshold * 100).toFixed(0)}%`);
|
|
5390
|
+
console.log(` ${chalk9.green("\u901A\u8FC7:")} ${passed.length}`);
|
|
5391
|
+
console.log(` ${chalk9.red("\u5931\u8D25:")} ${failed.length}`);
|
|
5275
5392
|
if (passed.length > 0) {
|
|
5276
5393
|
console.log();
|
|
5277
|
-
console.log(
|
|
5394
|
+
console.log(chalk9.green(" \u901A\u8FC7\u7684\u622A\u56FE:"));
|
|
5278
5395
|
for (const result of passed) {
|
|
5279
5396
|
const name = path15.basename(result.baselinePath);
|
|
5280
5397
|
if (result.totalPixels > 0) {
|
|
5281
5398
|
const diffPct = (result.diffRatio * 100).toFixed(2);
|
|
5282
|
-
console.log(
|
|
5399
|
+
console.log(chalk9.green(` \u2713 ${name} (\u5DEE\u5F02: ${diffPct}%)`));
|
|
5283
5400
|
} else {
|
|
5284
|
-
console.log(
|
|
5401
|
+
console.log(chalk9.green(` \u2713 ${name} (\u65B0\u5EFA\u57FA\u7EBF)`));
|
|
5285
5402
|
}
|
|
5286
5403
|
}
|
|
5287
5404
|
}
|
|
5288
5405
|
if (failed.length > 0) {
|
|
5289
5406
|
console.log();
|
|
5290
|
-
console.log(
|
|
5407
|
+
console.log(chalk9.red(" \u5931\u8D25\u7684\u622A\u56FE:"));
|
|
5291
5408
|
for (const result of failed) {
|
|
5292
5409
|
const name = path15.basename(result.baselinePath);
|
|
5293
5410
|
if (result.diffPixels === -1) {
|
|
5294
|
-
console.log(
|
|
5411
|
+
console.log(chalk9.red(` \u2717 ${name} (\u5C3A\u5BF8\u4E0D\u5339\u914D)`));
|
|
5295
5412
|
} else {
|
|
5296
5413
|
const diffPct = (result.diffRatio * 100).toFixed(2);
|
|
5297
|
-
console.log(
|
|
5414
|
+
console.log(chalk9.red(` \u2717 ${name} (\u5DEE\u5F02: ${diffPct}%)`));
|
|
5298
5415
|
}
|
|
5299
5416
|
if (result.diffPath) {
|
|
5300
|
-
console.log(
|
|
5417
|
+
console.log(chalk9.gray(` \u5DEE\u5F02\u56FE: ${path15.relative(process.cwd(), result.diffPath)}`));
|
|
5301
5418
|
}
|
|
5302
5419
|
}
|
|
5303
5420
|
console.log();
|
|
5304
|
-
console.log(
|
|
5421
|
+
console.log(chalk9.yellow(" \u63D0\u793A: \u8FD0\u884C qat visual approve \u66F4\u65B0\u57FA\u7EBF"));
|
|
5305
5422
|
}
|
|
5306
|
-
console.log(
|
|
5423
|
+
console.log(chalk9.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5307
5424
|
console.log();
|
|
5308
5425
|
}
|
|
5309
5426
|
async function approveBaselines(baselineDir, diffDir) {
|
|
@@ -5322,7 +5439,7 @@ async function approveBaselines(baselineDir, diffDir) {
|
|
|
5322
5439
|
spinner.succeed(`\u5DF2\u66F4\u65B0 ${updated.length} \u4E2A\u57FA\u7EBF\u5FEB\u7167`);
|
|
5323
5440
|
console.log();
|
|
5324
5441
|
for (const file of updated) {
|
|
5325
|
-
console.log(
|
|
5442
|
+
console.log(chalk9.green(` \u2713 ${file}`));
|
|
5326
5443
|
}
|
|
5327
5444
|
console.log();
|
|
5328
5445
|
}
|
|
@@ -5332,14 +5449,14 @@ async function cleanAll(baselineDir, diffDir) {
|
|
|
5332
5449
|
const diffs = cleanDiffs(diffDir);
|
|
5333
5450
|
spinner.succeed("\u6E05\u7406\u5B8C\u6210");
|
|
5334
5451
|
console.log();
|
|
5335
|
-
console.log(
|
|
5452
|
+
console.log(chalk9.white(" \u5DF2\u5220\u9664:"));
|
|
5336
5453
|
console.log(` \u57FA\u7EBF: ${baselines} \u4E2A`);
|
|
5337
5454
|
console.log(` \u5DEE\u5F02: ${diffs} \u4E2A`);
|
|
5338
5455
|
console.log();
|
|
5339
5456
|
}
|
|
5340
5457
|
|
|
5341
5458
|
// src/commands/setup.ts
|
|
5342
|
-
import
|
|
5459
|
+
import chalk10 from "chalk";
|
|
5343
5460
|
import inquirer4 from "inquirer";
|
|
5344
5461
|
import ora8 from "ora";
|
|
5345
5462
|
import { execFile as execFile4 } from "child_process";
|
|
@@ -5368,7 +5485,7 @@ function registerSetupCommand(program2) {
|
|
|
5368
5485
|
try {
|
|
5369
5486
|
await executeSetup(options);
|
|
5370
5487
|
} catch (error) {
|
|
5371
|
-
console.error(
|
|
5488
|
+
console.error(chalk10.red(`
|
|
5372
5489
|
\u2717 ${error instanceof Error ? error.message : String(error)}
|
|
5373
5490
|
`));
|
|
5374
5491
|
process.exit(1);
|
|
@@ -5376,20 +5493,20 @@ function registerSetupCommand(program2) {
|
|
|
5376
5493
|
});
|
|
5377
5494
|
}
|
|
5378
5495
|
async function executeSetup(options) {
|
|
5379
|
-
console.log(
|
|
5496
|
+
console.log(chalk10.cyan("\n QAT \u4F9D\u8D56\u5B89\u88C5\u5668\n"));
|
|
5380
5497
|
const projectInfo = detectProject();
|
|
5381
5498
|
if (!fs15.existsSync(path16.join(process.cwd(), "package.json"))) {
|
|
5382
5499
|
throw new Error("\u672A\u627E\u5230 package.json\uFF0C\u8BF7\u5728\u9879\u76EE\u6839\u76EE\u5F55\u6267\u884C\u6B64\u547D\u4EE4");
|
|
5383
5500
|
}
|
|
5384
5501
|
if (projectInfo.frameworkConfidence > 0) {
|
|
5385
|
-
console.log(
|
|
5502
|
+
console.log(chalk10.white(` \u68C0\u6D4B\u5230\u6846\u67B6: ${chalk10.cyan(projectInfo.frameworkDisplayName)}`));
|
|
5386
5503
|
if (projectInfo.uiLibrary !== "none") {
|
|
5387
|
-
console.log(
|
|
5504
|
+
console.log(chalk10.white(` UI \u7EC4\u4EF6\u5E93: ${chalk10.cyan(projectInfo.uiLibrary)}`));
|
|
5388
5505
|
}
|
|
5389
5506
|
if (projectInfo.monorepo !== "none") {
|
|
5390
|
-
console.log(
|
|
5507
|
+
console.log(chalk10.white(` Monorepo: ${chalk10.cyan(projectInfo.monorepo)}`));
|
|
5391
5508
|
if (projectInfo.appDirs.length > 0) {
|
|
5392
|
-
console.log(
|
|
5509
|
+
console.log(chalk10.white(` \u5B50\u9879\u76EE: ${chalk10.gray(projectInfo.appDirs.join(", "))}`));
|
|
5393
5510
|
}
|
|
5394
5511
|
}
|
|
5395
5512
|
console.log();
|
|
@@ -5425,12 +5542,12 @@ async function executeSetup(options) {
|
|
|
5425
5542
|
}
|
|
5426
5543
|
const groupsToInstall = determineGroups(config, projectInfo, options.force);
|
|
5427
5544
|
if (groupsToInstall.length === 0) {
|
|
5428
|
-
console.log(
|
|
5545
|
+
console.log(chalk10.green(" \u2713 \u6240\u6709\u4F9D\u8D56\u5DF2\u5B89\u88C5\uFF0C\u65E0\u9700\u989D\u5916\u64CD\u4F5C\n"));
|
|
5429
5546
|
return;
|
|
5430
5547
|
}
|
|
5431
5548
|
const selectedGroups = await selectGroups(groupsToInstall, options.dryRun);
|
|
5432
5549
|
if (selectedGroups.length === 0) {
|
|
5433
|
-
console.log(
|
|
5550
|
+
console.log(chalk10.gray("\n \u5DF2\u53D6\u6D88\u5B89\u88C5\n"));
|
|
5434
5551
|
return;
|
|
5435
5552
|
}
|
|
5436
5553
|
const allPackages = selectedGroups.flatMap((g) => g.packages);
|
|
@@ -5444,19 +5561,19 @@ ${allPackages.map((p) => ` - ${p}`).join("\n")}`,
|
|
|
5444
5561
|
}
|
|
5445
5562
|
]);
|
|
5446
5563
|
if (!confirmed) {
|
|
5447
|
-
console.log(
|
|
5564
|
+
console.log(chalk10.gray("\n \u5DF2\u53D6\u6D88\u5B89\u88C5\n"));
|
|
5448
5565
|
return;
|
|
5449
5566
|
}
|
|
5450
5567
|
if (options.dryRun) {
|
|
5451
|
-
console.log(
|
|
5568
|
+
console.log(chalk10.yellow("\n [Dry Run] \u4EE5\u4E0B\u547D\u4EE4\u5C06\u88AB\u6267\u884C:\n"));
|
|
5452
5569
|
const pm = getPackageManager(projectInfo.packageManager);
|
|
5453
5570
|
const installCmd = pm === "npm" ? "npm install -D" : pm === "yarn" ? "yarn add -D" : pm === "pnpm" ? "pnpm add -D" : "bun add -D";
|
|
5454
5571
|
for (const group of selectedGroups) {
|
|
5455
5572
|
const dirHint = installDir !== process.cwd() ? ` (\u5728 ${path16.relative(process.cwd(), installDir) || installDir})` : "";
|
|
5456
|
-
console.log(
|
|
5573
|
+
console.log(chalk10.white(` ${installCmd} ${group.packages.join(" ")}${dirHint}`));
|
|
5457
5574
|
if (group.postInstall) {
|
|
5458
5575
|
for (const cmd of group.postInstall) {
|
|
5459
|
-
console.log(
|
|
5576
|
+
console.log(chalk10.white(` ${cmd}`));
|
|
5460
5577
|
}
|
|
5461
5578
|
}
|
|
5462
5579
|
}
|
|
@@ -5569,36 +5686,36 @@ ${stderr}` : "")));
|
|
|
5569
5686
|
});
|
|
5570
5687
|
}
|
|
5571
5688
|
function displaySetupResult(groups) {
|
|
5572
|
-
console.log(
|
|
5573
|
-
console.log(
|
|
5689
|
+
console.log(chalk10.green("\n \u2713 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210!\n"));
|
|
5690
|
+
console.log(chalk10.white(" \u5DF2\u5B89\u88C5:"));
|
|
5574
5691
|
for (const group of groups) {
|
|
5575
|
-
console.log(
|
|
5692
|
+
console.log(chalk10.cyan(`
|
|
5576
5693
|
${group.name}`));
|
|
5577
5694
|
for (const pkg of group.packages) {
|
|
5578
|
-
console.log(
|
|
5695
|
+
console.log(chalk10.gray(` \u2713 ${pkg}`));
|
|
5579
5696
|
}
|
|
5580
5697
|
if (group.postInstall) {
|
|
5581
5698
|
for (const cmd of group.postInstall) {
|
|
5582
|
-
console.log(
|
|
5699
|
+
console.log(chalk10.gray(` \u2713 ${cmd}`));
|
|
5583
5700
|
}
|
|
5584
5701
|
}
|
|
5585
5702
|
}
|
|
5586
5703
|
console.log();
|
|
5587
|
-
console.log(
|
|
5588
|
-
console.log(
|
|
5589
|
-
console.log(
|
|
5704
|
+
console.log(chalk10.cyan(" \u4E0B\u4E00\u6B65:"));
|
|
5705
|
+
console.log(chalk10.gray(" 1. \u8FD0\u884C qat create \u521B\u5EFA\u6D4B\u8BD5\u7528\u4F8B"));
|
|
5706
|
+
console.log(chalk10.gray(" 2. \u8FD0\u884C qat run \u6267\u884C\u6D4B\u8BD5"));
|
|
5590
5707
|
console.log();
|
|
5591
5708
|
}
|
|
5592
5709
|
|
|
5593
5710
|
// src/commands/status.ts
|
|
5594
|
-
import
|
|
5711
|
+
import chalk11 from "chalk";
|
|
5595
5712
|
import ora9 from "ora";
|
|
5596
5713
|
function registerStatusCommand(program2) {
|
|
5597
5714
|
program2.command("status").description("\u67E5\u770B QAT \u72B6\u6001 - AI \u6A21\u578B\u4FE1\u606F\u3001\u9879\u76EE\u914D\u7F6E").action(async (options) => {
|
|
5598
5715
|
try {
|
|
5599
5716
|
await executeStatus(options);
|
|
5600
5717
|
} catch (error) {
|
|
5601
|
-
console.error(
|
|
5718
|
+
console.error(chalk11.red(`
|
|
5602
5719
|
\u2717 ${error instanceof Error ? error.message : String(error)}
|
|
5603
5720
|
`));
|
|
5604
5721
|
process.exit(1);
|
|
@@ -5606,24 +5723,24 @@ function registerStatusCommand(program2) {
|
|
|
5606
5723
|
});
|
|
5607
5724
|
}
|
|
5608
5725
|
async function executeStatus(_options) {
|
|
5609
|
-
console.log(
|
|
5610
|
-
console.log(
|
|
5726
|
+
console.log(chalk11.cyan("\n AI \u6A21\u578B\u72B6\u6001"));
|
|
5727
|
+
console.log(chalk11.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5611
5728
|
const globalAI = loadGlobalAIConfig();
|
|
5612
5729
|
if (!globalAI) {
|
|
5613
|
-
console.log(
|
|
5614
|
-
console.log(
|
|
5730
|
+
console.log(chalk11.yellow(" \u2717 \u672A\u914D\u7F6E AI \u6A21\u578B"));
|
|
5731
|
+
console.log(chalk11.gray(" \u8FD0\u884C qat change \u914D\u7F6E AI \u6A21\u578B"));
|
|
5615
5732
|
} else {
|
|
5616
|
-
console.log(` ${
|
|
5617
|
-
console.log(` ${
|
|
5618
|
-
console.log(` ${
|
|
5619
|
-
console.log(` ${
|
|
5620
|
-
console.log(` ${
|
|
5733
|
+
console.log(` ${chalk11.white("\u6A21\u578B:")} ${chalk11.green(globalAI.model)}`);
|
|
5734
|
+
console.log(` ${chalk11.white("API URL:")} ${chalk11.gray(globalAI.baseUrl)}`);
|
|
5735
|
+
console.log(` ${chalk11.white("API Key:")} ${chalk11.gray(maskApiKey(globalAI.apiKey))}`);
|
|
5736
|
+
console.log(` ${chalk11.white("Provider:")} ${chalk11.gray(globalAI.provider)}`);
|
|
5737
|
+
console.log(` ${chalk11.white("\u914D\u7F6E\u6587\u4EF6:")} ${chalk11.gray(getAIConfigPath())}`);
|
|
5621
5738
|
const testSpinner = ora9(" \u6B63\u5728\u6D4B\u8BD5\u8FDE\u901A\u6027...").start();
|
|
5622
5739
|
try {
|
|
5623
5740
|
const aiConfig = toAIConfig(globalAI);
|
|
5624
5741
|
const result = await testAIConnection(aiConfig);
|
|
5625
5742
|
if (result.ok) {
|
|
5626
|
-
testSpinner.succeed(` \u8FDE\u901A\u6B63\u5E38 ${
|
|
5743
|
+
testSpinner.succeed(` \u8FDE\u901A\u6B63\u5E38 ${chalk11.gray(`(${result.latencyMs}ms)`)}`);
|
|
5627
5744
|
} else {
|
|
5628
5745
|
testSpinner.fail(` \u8FDE\u901A\u5F02\u5E38: ${result.message}`);
|
|
5629
5746
|
}
|
|
@@ -5632,26 +5749,26 @@ async function executeStatus(_options) {
|
|
|
5632
5749
|
}
|
|
5633
5750
|
}
|
|
5634
5751
|
console.log();
|
|
5635
|
-
console.log(
|
|
5636
|
-
console.log(
|
|
5752
|
+
console.log(chalk11.cyan(" \u9879\u76EE\u914D\u7F6E"));
|
|
5753
|
+
console.log(chalk11.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5637
5754
|
try {
|
|
5638
5755
|
const config = await loadConfig();
|
|
5639
5756
|
const projectInfo = detectProject();
|
|
5640
|
-
console.log(` ${
|
|
5641
|
-
console.log(` ${
|
|
5642
|
-
console.log(` ${
|
|
5643
|
-
console.log(` ${
|
|
5644
|
-
console.log(` ${
|
|
5645
|
-
console.log(` ${
|
|
5646
|
-
console.log(` ${
|
|
5757
|
+
console.log(` ${chalk11.white("\u6846\u67B6:")} ${projectInfo.frameworkDisplayName}`);
|
|
5758
|
+
console.log(` ${chalk11.white("\u6E90\u7801\u76EE\u5F55:")} ${config.project.srcDir}`);
|
|
5759
|
+
console.log(` ${chalk11.white("Vitest:")} ${config.vitest.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")} (${config.vitest.environment})`);
|
|
5760
|
+
console.log(` ${chalk11.white("Playwright:")} ${config.playwright.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")} (${config.playwright.browsers.join(", ")})`);
|
|
5761
|
+
console.log(` ${chalk11.white("Mock:")} ${config.mock.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")} (port ${config.mock.port})`);
|
|
5762
|
+
console.log(` ${chalk11.white("Visual:")} ${config.visual.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")}`);
|
|
5763
|
+
console.log(` ${chalk11.white("Lighthouse:")} ${config.lighthouse.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")}`);
|
|
5647
5764
|
} catch {
|
|
5648
|
-
console.log(
|
|
5765
|
+
console.log(chalk11.yellow(" \u2717 \u672A\u627E\u5230\u9879\u76EE\u914D\u7F6E (\u8FD0\u884C qat init \u521D\u59CB\u5316)"));
|
|
5649
5766
|
}
|
|
5650
5767
|
console.log();
|
|
5651
5768
|
}
|
|
5652
5769
|
|
|
5653
5770
|
// src/commands/change.ts
|
|
5654
|
-
import
|
|
5771
|
+
import chalk12 from "chalk";
|
|
5655
5772
|
import inquirer5 from "inquirer";
|
|
5656
5773
|
import ora10 from "ora";
|
|
5657
5774
|
function registerChangeCommand(program2) {
|
|
@@ -5659,7 +5776,7 @@ function registerChangeCommand(program2) {
|
|
|
5659
5776
|
try {
|
|
5660
5777
|
await executeChange(options);
|
|
5661
5778
|
} catch (error) {
|
|
5662
|
-
console.error(
|
|
5779
|
+
console.error(chalk12.red(`
|
|
5663
5780
|
\u2717 ${error instanceof Error ? error.message : String(error)}
|
|
5664
5781
|
`));
|
|
5665
5782
|
process.exit(1);
|
|
@@ -5669,13 +5786,13 @@ function registerChangeCommand(program2) {
|
|
|
5669
5786
|
async function executeChange(_options) {
|
|
5670
5787
|
const current = loadGlobalAIConfig();
|
|
5671
5788
|
if (current) {
|
|
5672
|
-
console.log(
|
|
5673
|
-
console.log(` ${
|
|
5674
|
-
console.log(` ${
|
|
5675
|
-
console.log(` ${
|
|
5789
|
+
console.log(chalk12.cyan("\n \u5F53\u524D AI \u914D\u7F6E:"));
|
|
5790
|
+
console.log(` ${chalk12.white("\u6A21\u578B:")} ${chalk12.green(current.model)}`);
|
|
5791
|
+
console.log(` ${chalk12.white("API URL:")} ${chalk12.gray(current.baseUrl)}`);
|
|
5792
|
+
console.log(` ${chalk12.white("API Key:")} ${chalk12.gray(maskApiKey(current.apiKey))}`);
|
|
5676
5793
|
console.log();
|
|
5677
5794
|
} else {
|
|
5678
|
-
console.log(
|
|
5795
|
+
console.log(chalk12.yellow("\n \u5F53\u524D\u672A\u914D\u7F6E AI \u6A21\u578B\uFF0C\u8BF7\u914D\u7F6E:\n"));
|
|
5679
5796
|
}
|
|
5680
5797
|
const answers = await inquirer5.prompt([
|
|
5681
5798
|
{
|
|
@@ -5713,16 +5830,16 @@ async function executeChange(_options) {
|
|
|
5713
5830
|
model: answers.model?.trim() || "deepseek-chat"
|
|
5714
5831
|
};
|
|
5715
5832
|
saveGlobalAIConfig(newConfig);
|
|
5716
|
-
console.log(
|
|
5833
|
+
console.log(chalk12.green(`
|
|
5717
5834
|
\u2713 AI \u914D\u7F6E\u5DF2\u4FDD\u5B58`));
|
|
5718
|
-
console.log(
|
|
5719
|
-
console.log(` ${
|
|
5835
|
+
console.log(chalk12.gray(` ${getAIConfigPath()}`));
|
|
5836
|
+
console.log(` ${chalk12.white("\u6A21\u578B:")} ${chalk12.green(newConfig.model)} @ ${chalk12.gray(newConfig.baseUrl)}`);
|
|
5720
5837
|
const testSpinner = ora10(" \u6B63\u5728\u6D4B\u8BD5\u8FDE\u901A\u6027...").start();
|
|
5721
5838
|
try {
|
|
5722
5839
|
const aiConfig = toAIConfig(newConfig);
|
|
5723
5840
|
const result = await testAIConnection(aiConfig);
|
|
5724
5841
|
if (result.ok) {
|
|
5725
|
-
testSpinner.succeed(` AI \u8FDE\u901A\u6B63\u5E38 ${
|
|
5842
|
+
testSpinner.succeed(` AI \u8FDE\u901A\u6B63\u5E38 ${chalk12.gray(`(${newConfig.model}, ${result.latencyMs}ms)`)}`);
|
|
5726
5843
|
} else {
|
|
5727
5844
|
testSpinner.fail(` AI \u8FDE\u901A\u5F02\u5E38: ${result.message}`);
|
|
5728
5845
|
}
|
|
@@ -5733,37 +5850,40 @@ async function executeChange(_options) {
|
|
|
5733
5850
|
}
|
|
5734
5851
|
|
|
5735
5852
|
// src/cli.ts
|
|
5736
|
-
var VERSION = "0.3.
|
|
5853
|
+
var VERSION = "0.3.04";
|
|
5737
5854
|
function printLogo() {
|
|
5738
5855
|
const logo = `
|
|
5739
|
-
${
|
|
5740
|
-
${
|
|
5741
|
-
${
|
|
5742
|
-
${
|
|
5743
|
-
${
|
|
5744
|
-
${
|
|
5745
|
-
${
|
|
5856
|
+
${chalk13.bold.cyan(" ___ _ _ _ _ _____ _ _ ")}
|
|
5857
|
+
${chalk13.bold.cyan(" / _ \\ _ _ (_) ___ | | __ / \\ _ _ | |_ ___ |_ _| ___ ___ | |_ (_) _ __ __ _ ")}
|
|
5858
|
+
${chalk13.bold.cyan(" | | | | | | | | | | / __| | |/ / / _ \\ | | | | | __| / _ \\ | | / _ \\ / __| | __| | | | '_ \\ / _` |")}
|
|
5859
|
+
${chalk13.bold.cyan(" | |_| | | |_| | | | | (__ | < / ___ \\ | |_| | | |_ | (_) | | | | __/ \\__ \\ | |_ | | | | | | | (_| |")}
|
|
5860
|
+
${chalk13.bold.cyan(" \\__\\_\\ \\__,_| |_| \\___| |_|\\_\\ /_/ \\_\\ \\__,_| \\__| \\___/ |_| \\___| |___/ \\__| |_| |_| |_| \\__, |")}
|
|
5861
|
+
${chalk13.bold.cyan(" |___/ ")}
|
|
5862
|
+
${chalk13.gray(" CLI\u81EA\u52A8\u5316\u6D4B\u8BD5\u5DE5\u5177 v")}${chalk13.green(VERSION)}
|
|
5746
5863
|
`;
|
|
5747
5864
|
console.log(logo);
|
|
5748
5865
|
}
|
|
5749
5866
|
var program = new Command();
|
|
5750
|
-
program.name("qat").description("CLI\u81EA\u52A8\u5316\u6D4B\u8BD5\u5DE5\u5177 - \u9762\u5411Vue\u9879\u76EE\uFF0C\u96C6\u6210Vitest\u3001Playwright\uFF0C\u8986\u76D6\u6D4B\u8BD5\u5168\u6D41\u7A0B").version(VERSION).option("-c, --config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("-v, --verbose", "\u663E\u793A\u8BE6\u7EC6\u8F93\u51FA").hook("preAction", async (thisCommand) => {
|
|
5867
|
+
program.name("qat").description("CLI\u81EA\u52A8\u5316\u6D4B\u8BD5\u5DE5\u5177 - \u9762\u5411Vue\u9879\u76EE\uFF0C\u96C6\u6210Vitest\u3001Playwright\uFF0C\u8986\u76D6\u6D4B\u8BD5\u5168\u6D41\u7A0B").version(VERSION).option("-c, --config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("-v, --verbose", "\u663E\u793A\u8BE6\u7EC6\u8F93\u51FA").option("-d, --debug", "\u8C03\u8BD5\u6A21\u5F0F\uFF1A\u663E\u793AAI\u8BF7\u6C42/\u54CD\u5E94\u8BE6\u60C5").hook("preAction", async (thisCommand) => {
|
|
5751
5868
|
printLogo();
|
|
5752
5869
|
const opts = thisCommand.opts();
|
|
5753
5870
|
if (opts.verbose) {
|
|
5754
5871
|
process.env.QAT_VERBOSE = "true";
|
|
5755
5872
|
}
|
|
5873
|
+
if (opts.debug) {
|
|
5874
|
+
process.env.QAT_DEBUG = "true";
|
|
5875
|
+
}
|
|
5756
5876
|
if (opts.config) {
|
|
5757
5877
|
process.env.QAT_CONFIG_PATH = opts.config;
|
|
5758
5878
|
}
|
|
5759
5879
|
const { loadExternalFrameworks } = await import("./framework-registry-YGZ63RDX.js");
|
|
5760
5880
|
const loadResult = await loadExternalFrameworks(process.cwd());
|
|
5761
5881
|
if (loadResult.loaded > 0 && opts.verbose) {
|
|
5762
|
-
console.log(
|
|
5882
|
+
console.log(chalk13.gray(` [ext] \u5DF2\u52A0\u8F7D ${loadResult.loaded} \u4E2A\u5916\u90E8\u6269\u5C55\u6587\u4EF6: ${loadResult.files.join(", ")}`));
|
|
5763
5883
|
}
|
|
5764
5884
|
if (loadResult.errors.length > 0) {
|
|
5765
5885
|
for (const err of loadResult.errors) {
|
|
5766
|
-
console.log(
|
|
5886
|
+
console.log(chalk13.yellow(` [ext] \u8B66\u544A: ${err.file} - ${err.error}`));
|
|
5767
5887
|
}
|
|
5768
5888
|
}
|
|
5769
5889
|
});
|
|
@@ -5777,9 +5897,9 @@ registerSetupCommand(program);
|
|
|
5777
5897
|
registerStatusCommand(program);
|
|
5778
5898
|
registerChangeCommand(program);
|
|
5779
5899
|
program.on("command:*", (operands) => {
|
|
5780
|
-
console.error(
|
|
5900
|
+
console.error(chalk13.red(`
|
|
5781
5901
|
\u672A\u77E5\u547D\u4EE4: ${operands[0]}`));
|
|
5782
|
-
console.log(
|
|
5902
|
+
console.log(chalk13.gray(` \u4F7F\u7528 qat --help \u67E5\u770B\u53EF\u7528\u547D\u4EE4
|
|
5783
5903
|
`));
|
|
5784
5904
|
process.exit(1);
|
|
5785
5905
|
});
|