qat-cli 0.3.2 → 0.3.3

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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **Quick Auto Testing — 面向 Vue 项目,集成 Vitest & Playwright,AI 驱动覆盖测试全流程**
6
6
 
7
- [![npm version](https://img.shields.io/badge/version-0.3.02-blue.svg)](https://www.npmjs.com/package/qat-cli)
7
+ [![npm version](https://img.shields.io/badge/version-0.3.03-blue.svg)](https://www.npmjs.com/package/qat-cli)
8
8
  [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-green.svg)](https://nodejs.org/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.6-blue.svg)](https://www.typescriptlang.org/)
package/dist/cli.js CHANGED
@@ -1017,8 +1017,13 @@ ${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
1017
1017
  6. \u5982\u679C\u6709 props/emits \u4FE1\u606F\uFF0C\u52A1\u5FC5\u9488\u5BF9\u6BCF\u4E2A prop \u548C emit \u751F\u6210\u6D4B\u8BD5`;
1018
1018
  }
1019
1019
  buildGenerateTestUserPrompt(req) {
1020
+ const importPath = this.computeTestImportPath(req.type, req.target);
1020
1021
  let prompt = `\u8BF7\u4E3A\u4EE5\u4E0B\u6587\u4EF6\u751F\u6210${req.type}\u6D4B\u8BD5\u4EE3\u7801:
1021
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
1022
1027
  `;
1023
1028
  if (req.analysis) {
1024
1029
  prompt += "\n\u6E90\u7801\u5206\u6790\u7ED3\u679C:\n";
@@ -1068,6 +1073,46 @@ ${req.context}
1068
1073
  }
1069
1074
  return prompt;
1070
1075
  }
1076
+ /**
1077
+ * 根据测试类型和源文件路径,计算从测试文件到源文件的正确相对导入路径
1078
+ */
1079
+ computeTestImportPath(testType, targetPath) {
1080
+ if (!targetPath) return targetPath;
1081
+ const testDirMap = {
1082
+ unit: "tests/unit",
1083
+ component: "tests/component",
1084
+ e2e: "tests/e2e",
1085
+ api: "tests/api",
1086
+ visual: "tests/visual",
1087
+ performance: "tests/e2e"
1088
+ };
1089
+ const testDir = testDirMap[testType];
1090
+ if (!testDir) return targetPath;
1091
+ if (targetPath.startsWith("../") || targetPath.startsWith("./../")) {
1092
+ return targetPath;
1093
+ }
1094
+ const depth = testDir.split("/").length;
1095
+ const prefix = "../".repeat(depth);
1096
+ let cleanPath = targetPath.replace(/^\.\//, "");
1097
+ if (!cleanPath.endsWith(".vue")) {
1098
+ cleanPath = cleanPath.replace(/\.(ts|js|tsx|jsx)$/, "");
1099
+ }
1100
+ return `${prefix}${cleanPath}`;
1101
+ }
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
+ }
1071
1116
  parseGenerateTestResponse(content) {
1072
1117
  const codeBlockMatch = content.match(/```(?:typescript|ts|javascript|js)?\s*\n([\s\S]*?)```/);
1073
1118
  const code = codeBlockMatch ? codeBlockMatch[1].trim() : content.replace(/^(?:```[\s\S]*?\n)?/, "").replace(/\n?```$/, "").trim();
@@ -1096,10 +1141,12 @@ ISSUES: \u95EE\u9898\u5217\u8868\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF0C\u683C\u5F0F
1096
1141
  SUGGESTIONS: \u6539\u8FDB\u5EFA\u8BAE\u5217\u8868\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF0C\u683C\u5F0F "- \u5EFA\u8BAE\u63CF\u8FF0"\uFF09`;
1097
1142
  }
1098
1143
  buildReviewTestUserPrompt(req) {
1144
+ const importPath = this.computeTestImportPath(req.testType, req.target);
1099
1145
  let prompt = `\u8BF7\u5BA1\u67E5\u4EE5\u4E0B\u6D4B\u8BD5\u7528\u4F8B\u662F\u5426\u4E0E\u6E90\u7801\u8D34\u5207\u4E14\u51C6\u786E\u3002
1100
1146
 
1101
1147
  \u88AB\u6D4B\u6587\u4EF6: ${req.target}
1102
1148
  \u6D4B\u8BD5\u7C7B\u578B: ${req.testType}
1149
+ \u6B63\u786E\u7684 import \u8DEF\u5F84\u5E94\u4E3A: ${importPath}\uFF08\u6D4B\u8BD5\u6587\u4EF6\u4F4D\u4E8E ${this.getTestOutputDir(req.testType)}/\uFF09
1103
1150
 
1104
1151
  --- \u6E90\u7801\u5185\u5BB9 ---
1105
1152
  \`\`\`typescript
@@ -2082,9 +2129,33 @@ Handlebars.registerHelper("propTestValue", (prop) => {
2082
2129
  };
2083
2130
  return map[prop.type] || "'test-value'";
2084
2131
  });
2132
+ function resolveImportPath(testType, targetPath) {
2133
+ if (!targetPath) return targetPath;
2134
+ const testDirMap = {
2135
+ unit: "tests/unit",
2136
+ component: "tests/component",
2137
+ e2e: "tests/e2e",
2138
+ api: "tests/api",
2139
+ visual: "tests/visual",
2140
+ performance: "tests/e2e"
2141
+ };
2142
+ const testDir = testDirMap[testType];
2143
+ if (!testDir) return targetPath;
2144
+ if (targetPath.startsWith("../") || targetPath.startsWith("./../")) {
2145
+ return targetPath;
2146
+ }
2147
+ const depth = testDir.split("/").length;
2148
+ const prefix = "../".repeat(depth);
2149
+ let cleanPath = targetPath.replace(/^\.\//, "");
2150
+ if (!cleanPath.endsWith(".vue")) {
2151
+ cleanPath = cleanPath.replace(/\.(ts|js|tsx|jsx)$/, "");
2152
+ }
2153
+ return `${prefix}${cleanPath}`;
2154
+ }
2085
2155
  function renderTemplate(type, context) {
2086
2156
  const templateContent = loadTemplate(type);
2087
2157
  const template = Handlebars.compile(templateContent);
2158
+ const resolvedTarget = resolveImportPath(type, context.target);
2088
2159
  const fullContext = {
2089
2160
  vueVersion: 3,
2090
2161
  typescript: true,
@@ -2105,6 +2176,8 @@ function renderTemplate(type, context) {
2105
2176
  requiredProps: [],
2106
2177
  optionalProps: [],
2107
2178
  ...context,
2179
+ // 使用计算后的正确路径
2180
+ target: resolvedTarget,
2108
2181
  framework: context.framework || "vue",
2109
2182
  camelName: context.camelName || toCamelCase(context.name),
2110
2183
  pascalName: context.pascalName || toPascalCase(context.name)
@@ -2959,14 +3032,14 @@ function buildVitestArgs(options) {
2959
3032
  if (options.files && options.files.length > 0) {
2960
3033
  args.push(...options.files);
2961
3034
  } else {
2962
- const includeMap = {
2963
- unit: ["tests/unit"],
2964
- component: ["tests/component"],
2965
- api: ["tests/api"]
3035
+ const pathMap = {
3036
+ unit: "tests/unit/**/*.test.ts",
3037
+ component: "tests/component/**/*.test.ts",
3038
+ api: "tests/api/**/*.test.ts"
2966
3039
  };
2967
- const includes = includeMap[options.type];
2968
- if (includes) {
2969
- args.push("--include", includes.map((d) => `${d}/**/*.test.ts`).join(","));
3040
+ const testPattern = pathMap[options.type];
3041
+ if (testPattern) {
3042
+ args.push(testPattern);
2970
3043
  }
2971
3044
  }
2972
3045
  if (options.coverage) {
@@ -3990,7 +4063,7 @@ function registerRunCommand(program2) {
3990
4063
  });
3991
4064
  }
3992
4065
  async function executeRun(options) {
3993
- const { type, file, tag, watch, parallel, browser } = options;
4066
+ const { type, file, watch, parallel } = options;
3994
4067
  const config = await loadConfig(options.config);
3995
4068
  const globalAI = loadGlobalAIConfig();
3996
4069
  if (globalAI) {
@@ -4192,7 +4265,7 @@ function printDryRunCommands(types, options, config) {
4192
4265
  api: "tests/api"
4193
4266
  };
4194
4267
  const dir = dirMap[testType];
4195
- if (dir) cmd += ` --include '${dir}/**/*.test.ts'`;
4268
+ if (dir) cmd += ` '${dir}/**/*.test.ts'`;
4196
4269
  if (config.vitest.coverage) cmd += " --coverage";
4197
4270
  }
4198
4271
  break;
@@ -4306,7 +4379,7 @@ async function runTestType(testType, config, options) {
4306
4379
  throw new Error(`\u672A\u77E5\u7684\u8FD0\u884C\u5668: ${runner}`);
4307
4380
  }
4308
4381
  }
4309
- async function runWatchMode(config, options) {
4382
+ async function runWatchMode(_config, options) {
4310
4383
  console.log(chalk5.cyan(" \u76D1\u542C\u6A21\u5F0F\u5DF2\u542F\u52A8 (Ctrl+C \u9000\u51FA)\n"));
4311
4384
  const { spawn } = await import("child_process");
4312
4385
  const args = ["vitest", "--watch"];
@@ -4322,7 +4395,7 @@ async function runWatchMode(config, options) {
4322
4395
  };
4323
4396
  const dirs = types.map((t) => dirMap[t]).filter(Boolean);
4324
4397
  if (dirs.length > 0) {
4325
- args.push("--include", dirs.map((d) => `${d}/**/*.test.ts`).join(","));
4398
+ args.push(...dirs.map((d) => `${d}/**/*.test.ts`));
4326
4399
  }
4327
4400
  }
4328
4401
  const child = spawn("npx", args, {
@@ -5660,7 +5733,7 @@ async function executeChange(_options) {
5660
5733
  }
5661
5734
 
5662
5735
  // src/cli.ts
5663
- var VERSION = "0.3.02";
5736
+ var VERSION = "0.3.03";
5664
5737
  function printLogo() {
5665
5738
  const logo = `
5666
5739
  ${chalk12.bold.cyan(" ___ _ _ _ _ _____ _ _ ")}