qat-cli 0.3.1 → 0.3.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/services/config.ts","../src/services/detector.ts","../src/services/framework-registry.ts","../src/services/template.ts","../src/services/reporter.ts","../src/runners/vitest-runner.ts","../src/runners/playwright-runner.ts","../src/runners/lighthouse-runner.ts","../src/ai/noop-provider.ts","../src/ai/openai-provider.ts","../src/ai/provider.ts","../src/services/mock-server.ts","../src/services/visual.ts","../src/services/source-analyzer.ts"],"sourcesContent":["/**\r\n * QAT - CLI自动化测试工具\r\n * 面向Vue项目,集成Vitest、Playwright,覆盖测试全流程\r\n */\r\n\r\n// 导出类型\r\nexport type {\r\n TestType,\r\n TestStatus,\r\n TestCaseResult,\r\n TestError,\r\n TestSuiteResult,\r\n TestRunResult,\r\n CoverageResult,\r\n PerformanceMetrics,\r\n QATConfig,\r\n GlobalOptions,\r\n RunOptions,\r\n InitOptions,\r\n CreateOptions,\r\n MockOptions,\r\n VisualOptions,\r\n ReportOptions,\r\n SetupOptions,\r\n FrameworkType,\r\n UILibrary,\r\n MonorepoType,\r\n} from './types/index.js';\r\n\r\nexport type {\r\n AICapability,\r\n AIGenerateTestRequest,\r\n AIGenerateTestResponse,\r\n AIAnalyzeResultRequest,\r\n AIAnalyzeResultResponse,\r\n AIProvider,\r\n AIConfig,\r\n SourceAnalysisSummary,\r\n AIPresetProvider,\r\n} from './types/ai.js';\r\n\r\n// 导出配置服务\r\nexport {\r\n loadConfig,\r\n clearConfigCache,\r\n validateConfig,\r\n defineConfig,\r\n generateConfigFile,\r\n writeConfigFile,\r\n DEFAULT_CONFIG,\r\n} from './services/config.js';\r\n\r\n// 导出项目检测\r\nexport {\r\n detectProject,\r\n discoverVueComponents,\r\n discoverUtilityFiles,\r\n} from './services/detector.js';\r\nexport type { ProjectInfo } from './services/detector.js';\r\n\r\n// 导出模板引擎\r\nexport {\r\n renderTemplate,\r\n registerTemplate,\r\n generateTestFile,\r\n} from './services/template.js';\r\nexport type { TemplateContext } from './services/template.js';\r\n\r\n// 导出报告服务\r\nexport {\r\n aggregateResults,\r\n generateHTMLReport,\r\n writeReportToDisk,\r\n} from './services/reporter.js';\r\nexport type { ReportData } from './services/reporter.js';\r\n\r\n// 导出 Runner\r\nexport { runVitest } from './runners/vitest-runner.js';\r\nexport type { VitestRunnerOptions } from './runners/vitest-runner.js';\r\nexport { runPlaywright } from './runners/playwright-runner.js';\r\nexport type { PlaywrightRunnerOptions } from './runners/playwright-runner.js';\r\nexport { runLighthouse } from './runners/lighthouse-runner.js';\r\nexport type { LighthouseRunnerOptions } from './runners/lighthouse-runner.js';\r\n\r\n// 导出 AI Provider 管理\r\nexport {\r\n registerAIProvider,\r\n getRegisteredProviders,\r\n getAIProvider,\r\n resetAIProvider,\r\n isAIAvailable,\r\n testAIConnection,\r\n AI_PRESET_PROVIDERS,\r\n} from './ai/provider.js';\r\nexport type { AIProviderConstructor } from './ai/provider.js';\r\nexport { NoopAIProvider } from './ai/noop-provider.js';\r\nexport { OpenAICompatibleProvider } from './ai/openai-provider.js';\r\n\r\n// 导出 Mock 服务\r\nexport {\r\n getMockServerState,\r\n loadMockRoutes,\r\n startMockServer,\r\n stopMockServer,\r\n createDefaultRoutes,\r\n generateMockRouteTemplate,\r\n initMockRoutesDir,\r\n} from './services/mock-server.js';\r\nexport type { MockRoute, MockServerState } from './services/mock-server.js';\r\n\r\n// 导出视觉回归服务\r\nexport {\r\n compareImages,\r\n createBaseline,\r\n updateBaseline,\r\n updateAllBaselines,\r\n cleanBaselines,\r\n cleanDiffs,\r\n listBaselines,\r\n listDiffs,\r\n compareDirectories,\r\n} from './services/visual.js';\r\nexport type { DiffResult } from './services/visual.js';\r\n\r\n// 导出框架注册表\r\nexport {\r\n registerFramework,\r\n getRegisteredFrameworks,\r\n detectFramework,\r\n detectUILibrary,\r\n detectMonorepo,\r\n discoverAppDirs,\r\n loadExternalFrameworks,\r\n resetRegistry,\r\n} from './services/framework-registry.js';\r\nexport type {\r\n FrameworkDefinition,\r\n FrameworkDetectResult,\r\n DetectContext,\r\n ComponentTestSetup,\r\n LoadExternalFrameworksResult,\r\n} from './services/framework-registry.js';\r\n\r\n// 导出源码分析器\r\nexport {\r\n analyzeFile,\r\n analyzeFiles,\r\n scanAPICalls,\r\n generateMockRoutesFromAPICalls,\r\n} from './services/source-analyzer.js';\r\nexport type {\r\n ExportKind,\r\n ExportInfo,\r\n PropInfo,\r\n EmitInfo,\r\n VueComponentAnalysis,\r\n ModuleAnalysis,\r\n APICallInfo,\r\n MockRouteCandidate,\r\n} from './services/source-analyzer.js';\r\n","/**\r\n * 配置管理服务 - 读取/解析/校验/生成 qat.config.ts\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport chalk from 'chalk';\r\nimport type { QATConfig } from '../types/index.js';\r\n\r\n/** 默认配置 */\r\nexport const DEFAULT_CONFIG: QATConfig = {\r\n project: {\r\n framework: 'vue',\r\n vite: true,\r\n srcDir: 'src',\r\n },\r\n vitest: {\r\n enabled: true,\r\n coverage: true,\r\n globals: true,\r\n environment: 'happy-dom',\r\n },\r\n playwright: {\r\n enabled: true,\r\n browsers: ['chromium'],\r\n baseURL: 'http://localhost:5173',\r\n screenshot: 'only-on-failure',\r\n },\r\n visual: {\r\n enabled: true,\r\n threshold: 0.1,\r\n baselineDir: 'tests/visual/baseline',\r\n diffDir: 'tests/visual/diff',\r\n },\r\n lighthouse: {\r\n enabled: true,\r\n urls: ['http://localhost:5173'],\r\n runs: 3,\r\n thresholds: {\r\n performance: 80,\r\n accessibility: 90,\r\n },\r\n },\r\n mock: {\r\n enabled: true,\r\n port: 3456,\r\n routesDir: 'tests/mock/routes',\r\n },\r\n report: {\r\n outputDir: 'qat-report',\r\n open: false,\r\n },\r\n};\r\n\r\n/** 配置缓存 */\r\nlet cachedConfig: QATConfig | null = null;\r\n\r\n/**\r\n * defineConfig 辅助函数 - 提供类型提示,用于用户 qat.config.ts\r\n */\r\nexport function defineConfig(config: Partial<QATConfig>): Partial<QATConfig> {\r\n return config;\r\n}\r\n\r\n/**\r\n * 自动查找配置文件 (优先 .js,兼容 .ts)\r\n */\r\nfunction findConfigFile(): string {\r\n const cwd = process.cwd();\r\n const jsPath = path.join(cwd, 'qat.config.js');\r\n const tsPath = path.join(cwd, 'qat.config.ts');\r\n if (fs.existsSync(jsPath)) return jsPath;\r\n if (fs.existsSync(tsPath)) return tsPath;\r\n return 'qat.config.js';\r\n}\r\n\r\n/**\r\n * 加载配置文件\r\n * @param configPath 配置文件路径,默认为当前目录下 qat.config.ts\r\n * @param forceReload 强制重新加载(跳过缓存)\r\n */\r\nexport async function loadConfig(configPath?: string, forceReload = false): Promise<QATConfig> {\r\n if (cachedConfig && !forceReload) {\r\n return cachedConfig;\r\n }\r\n\r\n const filePath = configPath || process.env.QAT_CONFIG_PATH || findConfigFile();\r\n\r\n try {\r\n const configFile = await importConfig(filePath);\r\n const config = validateConfig(configFile);\r\n cachedConfig = config;\r\n return config;\r\n } catch (error) {\r\n if (isFileNotFoundError(error)) {\r\n if (process.env.QAT_VERBOSE === 'true') {\r\n console.log(chalk.yellow('未找到配置文件,使用默认配置'));\r\n }\r\n cachedConfig = { ...DEFAULT_CONFIG };\r\n return cachedConfig;\r\n }\r\n throw new Error(`配置文件加载失败: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n}\r\n\r\n/**\r\n * 清除配置缓存\r\n */\r\nexport function clearConfigCache(): void {\r\n cachedConfig = null;\r\n}\r\n\r\n/**\r\n * 动态导入配置文件\r\n */\r\nasync function importConfig(filePath: string): Promise<Partial<QATConfig>> {\r\n const { resolve } = await import('node:path');\r\n const { pathToFileURL } = await import('node:url');\r\n const absolutePath = resolve(process.cwd(), filePath);\r\n\r\n if (!fs.existsSync(absolutePath)) {\r\n throw new Error(`Cannot find module '${absolutePath}'`);\r\n }\r\n\r\n // Windows 兼容:ESM import() 需要 file:// URL,不能直接用绝对路径\r\n const fileUrl = pathToFileURL(absolutePath).href;\r\n\r\n try {\r\n const module = await import(fileUrl);\r\n return module.default || module;\r\n } catch {\r\n // 尝试 .js 扩展名(.ts 文件可能无法直接加载)\r\n const jsPath = absolutePath.replace(/\\.ts$/, '.js');\r\n if (jsPath !== absolutePath && fs.existsSync(jsPath)) {\r\n const jsUrl = pathToFileURL(jsPath).href;\r\n const module = await import(jsUrl);\r\n return module.default || module;\r\n }\r\n throw new Error(`无法加载配置文件: ${absolutePath}`);\r\n }\r\n}\r\n\r\n/**\r\n * 校验配置结构 - 深度合并默认配置并校验字段合法性\r\n */\r\nexport function validateConfig(config: Partial<QATConfig>): QATConfig {\r\n // 深度合并\r\n const merged: QATConfig = {\r\n project: { ...DEFAULT_CONFIG.project, ...config.project },\r\n vitest: { ...DEFAULT_CONFIG.vitest, ...config.vitest },\r\n playwright: { ...DEFAULT_CONFIG.playwright, ...config.playwright },\r\n visual: { ...DEFAULT_CONFIG.visual, ...config.visual },\r\n lighthouse: { ...DEFAULT_CONFIG.lighthouse, ...config.lighthouse },\r\n mock: { ...DEFAULT_CONFIG.mock, ...config.mock },\r\n report: { ...DEFAULT_CONFIG.report, ...config.report },\r\n };\r\n\r\n // AI 配置 - 始终合并,确保存在\r\n if (config.ai) {\r\n merged.ai = {\r\n provider: config.ai.provider || 'openai',\r\n apiKey: config.ai.apiKey,\r\n baseUrl: config.ai.baseUrl,\r\n model: config.ai.model,\r\n };\r\n }\r\n\r\n // 校验必填字段\r\n if (!merged.project.srcDir) {\r\n throw new Error('配置校验失败: project.srcDir 不能为空');\r\n }\r\n\r\n // 校验数值范围\r\n if (merged.visual.threshold < 0 || merged.visual.threshold > 1) {\r\n throw new Error('配置校验失败: visual.threshold 必须在 0-1 之间');\r\n }\r\n\r\n if (merged.lighthouse.runs < 1) {\r\n throw new Error('配置校验失败: lighthouse.runs 不能小于 1');\r\n }\r\n\r\n if (merged.mock.port < 1 || merged.mock.port > 65535) {\r\n throw new Error('配置校验失败: mock.port 必须在 1-65535 之间');\r\n }\r\n\r\n // 校验浏览器列表\r\n const validBrowsers = ['chromium', 'firefox', 'webkit'];\r\n for (const browser of merged.playwright.browsers) {\r\n if (!validBrowsers.includes(browser)) {\r\n throw new Error(`配置校验失败: 不支持的浏览器 \"${browser}\",可选值: ${validBrowsers.join(', ')}`);\r\n }\r\n }\r\n\r\n return merged;\r\n}\r\n\r\n/**\r\n * 生成 qat.config.js 文件内容\r\n * AI 配置存储在 ~/.qat/ai.json,不在此文件中展示\r\n */\r\nexport function generateConfigFile(overrides: Partial<QATConfig> = {}): string {\r\n const config = validateConfig(overrides);\r\n\r\n return `// @ts-check\r\n/**\r\n * QAT 配置文件 - Quick Auto Testing\r\n * 修改后无需重启,下次运行 qat 命令时自动生效\r\n *\r\n * AI 模型配置请运行: qat change\r\n * AI 状态查看请运行: qat status\r\n */\r\nimport { defineConfig } from 'qat-cli';\r\n\r\nexport default defineConfig({\r\n project: {\r\n framework: '${config.project.framework}',\r\n vite: ${config.project.vite ?? true},\r\n srcDir: '${config.project.srcDir}',\r\n },\r\n vitest: {\r\n enabled: ${config.vitest.enabled},\r\n coverage: ${config.vitest.coverage},\r\n globals: ${config.vitest.globals},\r\n environment: '${config.vitest.environment}',\r\n },\r\n playwright: {\r\n enabled: ${config.playwright.enabled},\r\n browsers: [${config.playwright.browsers.map(b => `'${b}'`).join(', ')}],\r\n baseURL: '${config.playwright.baseURL}',\r\n screenshot: '${config.playwright.screenshot}',\r\n },\r\n visual: {\r\n enabled: ${config.visual.enabled},\r\n threshold: ${config.visual.threshold},\r\n baselineDir: '${config.visual.baselineDir}',\r\n diffDir: '${config.visual.diffDir}',\r\n },\r\n lighthouse: {\r\n enabled: ${config.lighthouse.enabled},\r\n urls: [${config.lighthouse.urls.map(u => `'${u}'`).join(', ')}],\r\n runs: ${config.lighthouse.runs},\r\n thresholds: {${Object.entries(config.lighthouse.thresholds)\r\n .map(([k, v]) => `\\n ${k}: ${v},`)\r\n .join('')}\\n },\r\n },\r\n mock: {\r\n enabled: ${config.mock.enabled},\r\n port: ${config.mock.port},\r\n routesDir: '${config.mock.routesDir}',\r\n },\r\n report: {\r\n outputDir: '${config.report.outputDir}',\r\n open: ${config.report.open},\r\n },\r\n});\r\n`;\r\n}\r\n\r\n/**\r\n * 写入配置文件到磁盘\r\n */\r\nexport async function writeConfigFile(\r\n cwd: string,\r\n overrides: Partial<QATConfig> = {},\r\n force = false,\r\n): Promise<string> {\r\n const configPath = path.join(cwd, 'qat.config.js');\r\n\r\n if (fs.existsSync(configPath) && !force) {\r\n throw new Error(`配置文件已存在: ${configPath},使用 --force 覆盖`);\r\n }\r\n\r\n const content = generateConfigFile(overrides);\r\n fs.writeFileSync(configPath, content, 'utf-8');\r\n\r\n return configPath;\r\n}\r\n\r\nfunction isFileNotFoundError(error: unknown): boolean {\r\n if (error instanceof Error) {\r\n return (\r\n error.message.includes('Cannot find') ||\r\n error.message.includes('ENOENT') ||\r\n error.message.includes('无法加载配置文件')\r\n );\r\n }\r\n return false;\r\n}\r\n","/**\r\n * 项目检测服务 - 识别框架版本、依赖、目录结构、组件发现\r\n * 集成框架注册表,支持 Vue / Vben / Nuxt 等框架自动检测\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport type { FrameworkType, UILibrary, MonorepoType } from '../types/index.js';\r\nimport { detectFramework, discoverAppDirs, detectUILibrary, detectMonorepo } from './framework-registry.js';\r\nimport type { ComponentTestSetup } from './framework-registry.js';\r\n\r\nexport interface ProjectInfo {\r\n /** 是否为 Vue 项目 */\r\n isVue: boolean;\r\n /** 是否使用 Vite 构建 */\r\n isVite: boolean;\r\n /** Vue 主版本号 */\r\n vueVersion?: 2 | 3;\r\n /** 是否使用 TypeScript */\r\n typescript: boolean;\r\n /** 源码目录 */\r\n srcDir: string;\r\n /** 包管理器 */\r\n packageManager: 'npm' | 'yarn' | 'pnpm' | 'bun';\r\n /** 所有依赖名列表 */\r\n dependencies: string[];\r\n /** 组件目录列表 */\r\n componentDirs: string[];\r\n /** 页面/视图目录列表 */\r\n pageDirs: string[];\r\n /** 是否已有测试目录 */\r\n hasTests: boolean;\r\n /** 已有的测试框架 */\r\n testFrameworks: string[];\r\n /** 项目名称 */\r\n name: string;\r\n /** 检测到的框架类型 */\r\n framework: FrameworkType;\r\n /** 框架显示名称 */\r\n frameworkDisplayName: string;\r\n /** UI 组件库 */\r\n uiLibrary: UILibrary;\r\n /** Monorepo 类型 */\r\n monorepo: MonorepoType;\r\n /** Monorepo 子项目路径 */\r\n appDirs: string[];\r\n /** 框架检测置信度 */\r\n frameworkConfidence: number;\r\n /** 组件测试配置 */\r\n componentTestSetup?: ComponentTestSetup;\r\n}\r\n\r\n/**\r\n * 检测 Vue 版本 — 兼容 Monorepo,从子项目 package.json 中读取\r\n * 优先级:子项目 vue 依赖 > 根目录 vue 依赖\r\n */\r\nfunction detectVueVersion(cwd: string, rootDeps: Record<string, string>): 2 | 3 | null {\r\n // 1. 先尝试根目录\r\n if (rootDeps['vue']) {\r\n const v = parseVueMajorVersion(rootDeps['vue']);\r\n if (v) return v;\r\n }\r\n\r\n // 2. Monorepo: 遍历子项目查找 vue 依赖\r\n const appDirs = discoverAppDirs(cwd);\r\n for (const appDir of appDirs) {\r\n const subPkgPath = path.join(cwd, appDir, 'package.json');\r\n if (fs.existsSync(subPkgPath)) {\r\n try {\r\n const subPkg = JSON.parse(fs.readFileSync(subPkgPath, 'utf-8'));\r\n const subDeps = {\r\n ...(subPkg.dependencies as Record<string, string>),\r\n ...(subPkg.devDependencies as Record<string, string>),\r\n };\r\n if (subDeps['vue']) {\r\n const v = parseVueMajorVersion(subDeps['vue']);\r\n if (v) return v;\r\n }\r\n } catch {\r\n // 解析失败跳过\r\n }\r\n }\r\n }\r\n\r\n // 3. 检查 packages/ 目录下的子包\r\n const packagesPath = path.join(cwd, 'packages');\r\n if (fs.existsSync(packagesPath)) {\r\n try {\r\n const entries = fs.readdirSync(packagesPath, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (!entry.isDirectory()) continue;\r\n const subPkgPath = path.join(packagesPath, entry.name, 'package.json');\r\n if (fs.existsSync(subPkgPath)) {\r\n try {\r\n const subPkg = JSON.parse(fs.readFileSync(subPkgPath, 'utf-8'));\r\n const subDeps = {\r\n ...(subPkg.dependencies as Record<string, string>),\r\n ...(subPkg.devDependencies as Record<string, string>),\r\n };\r\n if (subDeps['vue']) {\r\n const v = parseVueMajorVersion(subDeps['vue']);\r\n if (v) return v;\r\n }\r\n } catch {\r\n // 解析失败跳过\r\n }\r\n }\r\n }\r\n } catch {\r\n // 目录读取失败跳过\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * 从 vue 依赖版本字符串中解析主版本号\r\n */\r\nfunction parseVueMajorVersion(versionStr: string): 2 | 3 | null {\r\n const clean = versionStr.replace(/^[\\^~>=<\\s]+/, '');\r\n // 处理 workspace:* 或 file: 协议 — 无法从版本号判断,返回 null 让后续逻辑推断\r\n if (clean === '*' || clean.startsWith('workspace:') || clean.startsWith('file:')) {\r\n return null;\r\n }\r\n const major = parseInt(clean.split('.')[0], 10);\r\n if (isNaN(major)) return null;\r\n return major >= 3 ? 3 : 2;\r\n}\r\n\r\n/**\r\n * 当版本号无法直接解析时,通过其他线索推断 Vue 主版本号\r\n * - 检查 vue-demi(Vue 2/3 通用库,通常与 Vue 2 组合式 API 一起使用)\r\n * - 检查 @vue/composition-api(仅 Vue 2)\r\n * - 检查 nuxt(Nuxt 3 = Vue 3,Nuxt 2 = Vue 2)\r\n * - 默认推断为 Vue 3(2024+ 新项目的主流)\r\n */\r\nfunction inferVueVersion(cwd: string, allDeps: Record<string, string>): 2 | 3 {\r\n // Nuxt 3 = Vue 3\r\n if (allDeps['nuxt']) {\r\n const nuxtVer = allDeps['nuxt'].replace(/^[\\^~>=<\\s]+/, '');\r\n const major = parseInt(nuxtVer.split('.')[0], 10);\r\n if (!isNaN(major) && major >= 3) return 3;\r\n if (!isNaN(major) && major < 3) return 2;\r\n }\r\n\r\n // @vue/composition-api 仅 Vue 2 使用\r\n if (allDeps['@vue/composition-api']) return 2;\r\n\r\n // vue-demi 的存在暗示可能使用了组合式 API,但不确定版本\r\n // 检查是否有 Vue 3 特有的依赖\r\n const vue3Indicators = [\r\n 'vue-tsc', // Vue 3 TypeScript 支持\r\n '@vue/compiler-sfc', // Vue 3 SFC 编译器\r\n 'unplugin-vue', // Vue 3 Vite 插件\r\n 'vite-plugin-vue', // Vue 3 Vite 插件\r\n ];\r\n for (const dep of vue3Indicators) {\r\n if (allDeps[dep]) return 3;\r\n }\r\n\r\n // 检查 vite.config 文件中是否有 vue 插件\r\n const viteConfigPaths = ['vite.config.ts', 'vite.config.js'];\r\n for (const cfg of viteConfigPaths) {\r\n const cfgPath = path.join(cwd, cfg);\r\n if (fs.existsSync(cfgPath)) {\r\n try {\r\n const content = fs.readFileSync(cfgPath, 'utf-8');\r\n // @vitejs/plugin-vue2 或 vite-plugin-vue2 明确是 Vue 2\r\n if (content.includes('plugin-vue2') || content.includes('vite-plugin-vue2')) return 2;\r\n // @vitejs/plugin-vue 是 Vue 3\r\n if (content.includes('@vitejs/plugin-vue') || content.includes('unplugin-vue')) return 3;\r\n } catch {\r\n // 读取失败跳过\r\n }\r\n }\r\n }\r\n\r\n // 默认推断为 Vue 3\r\n return 3;\r\n}\r\n\r\n/**\r\n * 检测项目信息\r\n */\r\nexport function detectProject(cwd: string = process.cwd()): ProjectInfo {\r\n const info: ProjectInfo = {\r\n isVue: false,\r\n isVite: false,\r\n typescript: false,\r\n srcDir: 'src',\r\n packageManager: 'npm',\r\n dependencies: [],\r\n componentDirs: [],\r\n pageDirs: [],\r\n hasTests: false,\r\n testFrameworks: [],\r\n name: path.basename(cwd),\r\n framework: 'vue',\r\n frameworkDisplayName: 'Vue',\r\n uiLibrary: 'none',\r\n monorepo: 'none',\r\n appDirs: [],\r\n frameworkConfidence: 0,\r\n };\r\n\r\n // 读取 package.json\r\n const pkgPath = path.join(cwd, 'package.json');\r\n let pkg: Record<string, unknown> = {};\r\n let allDeps: Record<string, string> = {};\r\n\r\n if (fs.existsSync(pkgPath)) {\r\n try {\r\n pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\r\n } catch {\r\n // package.json 解析失败,跳过\r\n }\r\n\r\n allDeps = {\r\n ...(pkg.dependencies as Record<string, string>),\r\n ...(pkg.devDependencies as Record<string, string>),\r\n };\r\n\r\n info.dependencies = Object.keys(allDeps);\r\n info.name = (pkg.name as string) || info.name;\r\n\r\n // 检测 Vue(优先从子项目 package.json 读取,兼容 Monorepo)\r\n const vueVersion = detectVueVersion(cwd, allDeps);\r\n if (vueVersion) {\r\n info.isVue = true;\r\n info.vueVersion = vueVersion;\r\n } else if (allDeps['vue']) {\r\n // vue 依赖存在但版本号无法解析(如 workspace:*),仍标记为 Vue 项目\r\n info.isVue = true;\r\n info.vueVersion = inferVueVersion(cwd, allDeps);\r\n } else {\r\n // 无 vue 依赖,尝试从文件系统检测(项目中有 .vue 文件)\r\n info.isVue = detectVueByFileSystem(cwd);\r\n if (info.isVue) {\r\n info.vueVersion = inferVueVersion(cwd, allDeps);\r\n }\r\n }\r\n\r\n // 检测 Vite\r\n info.isVite = !!allDeps['vite'] || fs.existsSync(path.join(cwd, 'vite.config.ts')) || fs.existsSync(path.join(cwd, 'vite.config.js'));\r\n\r\n // 检测 TypeScript\r\n info.typescript =\r\n !!allDeps['typescript'] ||\r\n fs.existsSync(path.join(cwd, 'tsconfig.json')) ||\r\n fs.existsSync(path.join(cwd, 'tsconfig.app.json'));\r\n\r\n // 检测已有测试框架\r\n if (allDeps['vitest']) info.testFrameworks.push('vitest');\r\n if (allDeps['@playwright/test']) info.testFrameworks.push('playwright');\r\n if (allDeps['jest']) info.testFrameworks.push('jest');\r\n if (allDeps['cypress']) info.testFrameworks.push('cypress');\r\n if (allDeps['@vue/test-utils']) info.testFrameworks.push('@vue/test-utils');\r\n }\r\n\r\n // 获取根目录文件列表(用于框架检测)\r\n const rootFiles = getRootFiles(cwd);\r\n\r\n // 使用框架注册表检测\r\n const frameworkResult = detectFramework({\r\n cwd,\r\n dependencies: allDeps,\r\n rootFiles,\r\n });\r\n\r\n if (frameworkResult) {\r\n info.framework = frameworkResult.framework;\r\n info.frameworkDisplayName = frameworkResult.displayName;\r\n info.frameworkConfidence = frameworkResult.confidence;\r\n info.uiLibrary = frameworkResult.uiLibrary;\r\n info.monorepo = frameworkResult.monorepo;\r\n info.appDirs = frameworkResult.appDirs;\r\n info.srcDir = frameworkResult.srcDir;\r\n info.componentTestSetup = frameworkResult.componentTestSetup;\r\n\r\n // 框架注册表返回的目录\r\n info.componentDirs = frameworkResult.componentDirs.filter((d) =>\r\n fs.existsSync(path.join(cwd, d)),\r\n );\r\n info.pageDirs = frameworkResult.pageDirs.filter((d) =>\r\n fs.existsSync(path.join(cwd, d)),\r\n );\r\n\r\n // Vben / Nuxt 本质也是 Vue\r\n if (frameworkResult.framework === 'vben' || frameworkResult.framework === 'nuxt') {\r\n info.isVue = true;\r\n info.vueVersion = 3;\r\n }\r\n\r\n // 框架注册表检测到 Vue 类型时,确保 isVue 被标记\r\n if (frameworkResult.framework === 'vue' && !info.isVue) {\r\n info.isVue = true;\r\n }\r\n\r\n // 框架注册表检测到 Vue 但 vueVersion 未设置时,推断版本\r\n if (info.isVue && !info.vueVersion) {\r\n info.vueVersion = detectVueVersion(cwd, allDeps) || inferVueVersion(cwd, allDeps);\r\n }\r\n } else {\r\n // 回退到原始检测逻辑\r\n info.srcDir = detectSrcDir(cwd);\r\n info.uiLibrary = detectUILibrary(allDeps);\r\n info.monorepo = detectMonorepo(cwd, rootFiles);\r\n info.appDirs = discoverAppDirs(cwd);\r\n info.componentDirs = discoverComponentDirs(cwd, info.srcDir);\r\n info.pageDirs = discoverPageDirs(cwd, info.srcDir);\r\n }\r\n\r\n // 检测已有测试目录\r\n const possibleTestDirs = ['tests', 'test', '__tests__', 'spec'];\r\n for (const dir of possibleTestDirs) {\r\n if (fs.existsSync(path.join(cwd, dir))) {\r\n info.hasTests = true;\r\n break;\r\n }\r\n }\r\n\r\n // 检测包管理器\r\n if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {\r\n info.packageManager = 'pnpm';\r\n } else if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {\r\n info.packageManager = 'yarn';\r\n } else if (fs.existsSync(path.join(cwd, 'bun.lockb'))) {\r\n info.packageManager = 'bun';\r\n }\r\n\r\n return info;\r\n}\r\n\r\n/**\r\n * 通过文件系统检测是否为 Vue 项目\r\n * 当 package.json 中没有 vue 依赖时,检查源码目录中是否存在 .vue 文件\r\n */\r\nfunction detectVueByFileSystem(cwd: string): boolean {\r\n const possibleSrcDirs = ['src', 'lib', 'app'];\r\n for (const srcDir of possibleSrcDirs) {\r\n const srcPath = path.join(cwd, srcDir);\r\n if (fs.existsSync(srcPath)) {\r\n try {\r\n if (hasVueFilesInDir(srcPath, 2)) return true;\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n }\r\n }\r\n\r\n // 也检查根目录下的 components / pages\r\n const rootVueDirs = ['components', 'pages', 'views'];\r\n for (const dir of rootVueDirs) {\r\n const dirPath = path.join(cwd, dir);\r\n if (fs.existsSync(dirPath)) {\r\n try {\r\n if (hasVueFilesInDir(dirPath, 1)) return true;\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * 递归检查目录中是否存在 .vue 文件(限制递归深度防止性能问题)\r\n */\r\nfunction hasVueFilesInDir(dir: string, maxDepth: number): boolean {\r\n if (maxDepth < 0) return false;\r\n try {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (entry.name === 'node_modules' || entry.name === 'dist') continue;\r\n if (entry.isFile() && entry.name.endsWith('.vue')) return true;\r\n if (entry.isDirectory()) {\r\n if (hasVueFilesInDir(path.join(dir, entry.name), maxDepth - 1)) return true;\r\n }\r\n }\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * 获取根目录文件和目录列表\r\n */\r\nfunction getRootFiles(cwd: string): string[] {\r\n try {\r\n return fs.readdirSync(cwd);\r\n } catch {\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * 检测源码目录(回退逻辑)\r\n */\r\nfunction detectSrcDir(cwd: string): string {\r\n const possibleSrcDirs = ['src', 'lib', 'app'];\r\n for (const dir of possibleSrcDirs) {\r\n if (fs.existsSync(path.join(cwd, dir))) {\r\n return dir;\r\n }\r\n }\r\n return 'src';\r\n}\r\n\r\n/**\r\n * 发现 Vue 组件目录(回退逻辑)\r\n */\r\nfunction discoverComponentDirs(cwd: string, srcDir: string): string[] {\r\n const dirs: string[] = [];\r\n const srcPath = path.join(cwd, srcDir);\r\n\r\n if (!fs.existsSync(srcPath)) return dirs;\r\n\r\n const commonDirs = [\r\n 'components',\r\n 'src/components',\r\n 'src/views/components',\r\n 'src/shared/components',\r\n ];\r\n\r\n for (const dir of commonDirs) {\r\n const fullPath = path.join(cwd, dir);\r\n if (fs.existsSync(fullPath)) {\r\n dirs.push(dir);\r\n }\r\n }\r\n\r\n // 递归查找含 .vue 文件的目录\r\n try {\r\n const entries = fs.readdirSync(srcPath, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (entry.isDirectory() && !dirs.includes(`${srcDir}/${entry.name}`)) {\r\n const subDir = path.join(srcPath, entry.name);\r\n const hasVueFiles = fs.readdirSync(subDir).some((f) => f.endsWith('.vue'));\r\n if (hasVueFiles && entry.name !== 'node_modules') {\r\n dirs.push(`${srcDir}/${entry.name}`);\r\n }\r\n }\r\n }\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n\r\n return dirs;\r\n}\r\n\r\n/**\r\n * 发现页面/视图目录(回退逻辑)\r\n */\r\nfunction discoverPageDirs(cwd: string, _srcDir: string): string[] {\r\n const dirs: string[] = [];\r\n const commonDirs = ['pages', 'views', 'src/pages', 'src/views'];\r\n\r\n for (const dir of commonDirs) {\r\n if (fs.existsSync(path.join(cwd, dir))) {\r\n dirs.push(dir);\r\n }\r\n }\r\n\r\n return dirs;\r\n}\r\n\r\n/**\r\n * 发现项目中的 Vue 组件文件\r\n * @returns 组件文件相对路径列表\r\n */\r\nexport function discoverVueComponents(cwd: string, srcDir: string): string[] {\r\n const components: string[] = [];\r\n const searchPaths = [path.join(cwd, srcDir)];\r\n\r\n // Monorepo: 也扫描子项目\r\n const appDirs = discoverAppDirs(cwd);\r\n for (const appDir of appDirs) {\r\n const appSrcPath = path.join(cwd, appDir, 'src');\r\n if (fs.existsSync(appSrcPath)) {\r\n searchPaths.push(appSrcPath);\r\n }\r\n }\r\n\r\n for (const searchPath of searchPaths) {\r\n if (!fs.existsSync(searchPath)) continue;\r\n try {\r\n walkForVueFiles(searchPath, cwd, components);\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n }\r\n\r\n return components;\r\n}\r\n\r\n/**\r\n * 递归查找 .vue 文件\r\n */\r\nfunction walkForVueFiles(dir: string, cwd: string, result: string[]): void {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== 'dist') {\r\n walkForVueFiles(fullPath, cwd, result);\r\n } else if (entry.isFile() && entry.name.endsWith('.vue')) {\r\n result.push(path.relative(cwd, fullPath).replace(/\\\\/g, '/'));\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 发现项目中的工具函数/服务文件\r\n * @returns 文件相对路径列表\r\n */\r\nexport function discoverUtilityFiles(cwd: string, srcDir: string): string[] {\r\n const files: string[] = [];\r\n const searchPaths = [path.join(cwd, srcDir)];\r\n\r\n // Monorepo: 也扫描子项目和共享包\r\n const appDirs = discoverAppDirs(cwd);\r\n for (const appDir of appDirs) {\r\n const appSrcPath = path.join(cwd, appDir, 'src');\r\n if (fs.existsSync(appSrcPath)) {\r\n searchPaths.push(appSrcPath);\r\n }\r\n }\r\n\r\n const utilityPatterns = [\r\n /^(utils|helpers|services|composables|hooks|api|lib)/,\r\n ];\r\n\r\n for (const searchPath of searchPaths) {\r\n if (!fs.existsSync(searchPath)) continue;\r\n try {\r\n walkForUtilityFiles(searchPath, cwd, searchPath, utilityPatterns, files);\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n }\r\n\r\n return files;\r\n}\r\n\r\n/**\r\n * 递归查找工具函数文件\r\n */\r\nfunction walkForUtilityFiles(\r\n dir: string,\r\n cwd: string,\r\n rootSearchPath: string,\r\n patterns: RegExp[],\r\n result: string[],\r\n): void {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n if (entry.isDirectory()) {\r\n if (entry.name !== 'node_modules' && entry.name !== 'dist' && entry.name !== 'components') {\r\n const relativePath = path.relative(rootSearchPath, fullPath).replace(/\\\\/g, '/');\r\n if (patterns.some((p) => p.test(relativePath))) {\r\n walkForUtilityFiles(fullPath, cwd, rootSearchPath, patterns, result);\r\n }\r\n }\r\n } else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.js'))) {\r\n result.push(path.relative(cwd, fullPath).replace(/\\\\/g, '/'));\r\n }\r\n }\r\n}\r\n","/**\r\n * 框架注册表 - 可扩展的框架检测与适配系统\r\n *\r\n * 添加新框架只需调用 registerFramework() 注册即可,\r\n * detector / template / setup 等模块自动适配。\r\n */\r\n\r\nimport type { FrameworkType, UILibrary, MonorepoType } from '../types/index.js';\r\n\r\n/** 框架检测上下文 */\r\nexport interface DetectContext {\r\n /** 项目根目录 */\r\n cwd: string;\r\n /** package.json 中的所有依赖 */\r\n dependencies: Record<string, string>;\r\n /** 项目根目录下的文件/目录列表 */\r\n rootFiles: string[];\r\n}\r\n\r\n/** 框架检测结果 */\r\nexport interface FrameworkDetectResult {\r\n /** 框架类型 */\r\n framework: FrameworkType;\r\n /** 框架显示名称 */\r\n displayName: string;\r\n /** UI 组件库 */\r\n uiLibrary: UILibrary;\r\n /** 是否为 Monorepo */\r\n monorepo: MonorepoType;\r\n /** Monorepo 下的子项目路径列表 */\r\n appDirs: string[];\r\n /** 源码目录(相对于项目根或子项目根) */\r\n srcDir: string;\r\n /** 额外的组件目录 */\r\n componentDirs: string[];\r\n /** 额外的页面/视图目录 */\r\n pageDirs: string[];\r\n /** 检测置信度 0-1,值越高越匹配 */\r\n confidence: number;\r\n /** 组件测试需要的全局插件/注册 */\r\n componentTestSetup?: ComponentTestSetup;\r\n}\r\n\r\n/** 组件测试配置 */\r\nexport interface ComponentTestSetup {\r\n /** 需要全局安装的 Vue 插件 import 语句 */\r\n globalPlugins?: string[];\r\n /** 需要全局注册的组件 stub import 语句 */\r\n globalStubs?: string[];\r\n /** mount 时的额外配置代码片段 */\r\n mountOptions?: string;\r\n /** 额外的 import 语句 */\r\n extraImports?: string[];\r\n}\r\n\r\n/** 框架定义 */\r\nexport interface FrameworkDefinition {\r\n /** 框架标识 */\r\n type: FrameworkType;\r\n /** 框架显示名称 */\r\n displayName: string;\r\n /** 检测函数:返回置信度 0-1,0 表示不匹配 */\r\n detect: (ctx: DetectContext) => number;\r\n /** 框架检测结果补充(detect 返回 > 0 时调用) */\r\n resolve: (ctx: DetectContext) => Omit<FrameworkDetectResult, 'framework' | 'displayName' | 'confidence'>;\r\n}\r\n\r\n// ─── 框架注册表 ───────────────────────────────────────────────\r\n\r\nconst registry: FrameworkDefinition[] = [];\r\n\r\n/**\r\n * 注册框架定义\r\n */\r\nexport function registerFramework(definition: FrameworkDefinition): void {\r\n // 同类型框架替换,否则追加\r\n const idx = registry.findIndex((f) => f.type === definition.type);\r\n if (idx >= 0) {\r\n registry[idx] = definition;\r\n } else {\r\n registry.push(definition);\r\n }\r\n}\r\n\r\n/**\r\n * 获取所有已注册框架\r\n */\r\nexport function getRegisteredFrameworks(): FrameworkDefinition[] {\r\n return [...registry];\r\n}\r\n\r\n/**\r\n * 检测当前项目使用的框架\r\n * 返回置信度最高的框架检测结果\r\n */\r\nexport function detectFramework(ctx: DetectContext): FrameworkDetectResult | null {\r\n let best: { definition: FrameworkDefinition; confidence: number } | null = null;\r\n\r\n for (const definition of registry) {\r\n const confidence = definition.detect(ctx);\r\n if (confidence > 0 && (!best || confidence > best.confidence)) {\r\n best = { definition, confidence };\r\n }\r\n }\r\n\r\n if (!best) return null;\r\n\r\n const resolved = best.definition.resolve(ctx);\r\n return {\r\n framework: best.definition.type,\r\n displayName: best.definition.displayName,\r\n confidence: best.confidence,\r\n ...resolved,\r\n };\r\n}\r\n\r\n// ─── 工具函数 ─────────────────────────────────────────────────\r\n\r\n/**\r\n * 检测 Monorepo 类型\r\n */\r\nexport function detectMonorepo(_cwd: string, rootFiles: string[]): MonorepoType {\r\n // pnpm workspace\r\n if (rootFiles.includes('pnpm-workspace.yaml')) return 'pnpm-workspace';\r\n // Turborepo\r\n if (rootFiles.includes('turbo.json')) return 'turborepo';\r\n // Nx\r\n if (rootFiles.includes('nx.json')) return 'nx';\r\n // Lerna\r\n if (rootFiles.includes('lerna.json')) return 'lerna';\r\n return 'none';\r\n}\r\n\r\n/**\r\n * 检测 UI 组件库\r\n */\r\nexport function detectUILibrary(dependencies: Record<string, string>): UILibrary {\r\n if (dependencies['ant-design-vue']) return 'ant-design-vue';\r\n if (dependencies['element-plus']) return 'element-plus';\r\n if (dependencies['naive-ui']) return 'naive-ui';\r\n if (dependencies['vuetify']) return 'vuetify';\r\n if (dependencies['primevue']) return 'primevue';\r\n return 'none';\r\n}\r\n\r\n/**\r\n * 扫描 Monorepo 下的子项目目录\r\n */\r\nexport function discoverAppDirs(cwd: string): string[] {\r\n const appsPath = join(cwd, 'apps');\r\n const packagesPath = join(cwd, 'packages');\r\n\r\n const dirs: string[] = [];\r\n\r\n for (const base of [appsPath, packagesPath]) {\r\n try {\r\n const entries = fs.readdirSync(base, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (entry.isDirectory()) {\r\n const subPkgPath = join(base, entry.name, 'package.json');\r\n if (fs.existsSync(subPkgPath)) {\r\n dirs.push(base === appsPath\r\n ? `apps/${entry.name}`\r\n : `packages/${entry.name}`);\r\n }\r\n }\r\n }\r\n } catch {\r\n // 目录不存在或无权限\r\n }\r\n }\r\n\r\n return dirs;\r\n}\r\n\r\n// ─── 内置框架:Vben ────────────────────────────────────────────\r\n\r\nregisterFramework({\r\n type: 'vben',\r\n displayName: 'Vben Admin',\r\n detect(ctx) {\r\n const { dependencies, rootFiles } = ctx;\r\n // Vben 5.x 使用 @vben/cli\r\n if (dependencies['@vben/cli']) return 1.0;\r\n // Vben 5.x workspace 特征\r\n if (\r\n rootFiles.includes('pnpm-workspace.yaml') &&\r\n rootFiles.includes('apps') &&\r\n dependencies['@vben/types']\r\n ) return 0.95;\r\n // Vben 2.x / 3.x 特征(vite + ant-design-vue 的组合不一定是 vben,置信度降低)\r\n if (dependencies['vite'] && dependencies['ant-design-vue'] && dependencies['pinia']) return 0.3;\r\n return 0;\r\n },\r\n resolve(ctx) {\r\n const { cwd, dependencies } = ctx;\r\n const monorepo = detectMonorepo(cwd, ctx.rootFiles);\r\n const uiLibrary = detectUILibrary(dependencies);\r\n const appDirs = discoverAppDirs(cwd);\r\n\r\n // Vben 5.x 默认子项目命名\r\n const defaultAppNames = ['web-antd', 'web-ele', 'web-naive', 'web-en', 'admin'];\r\n let primaryApp = appDirs.find((d) =>\r\n defaultAppNames.some((name) => d.includes(name)),\r\n );\r\n\r\n // 如果没找到默认子项目,取 apps/ 下第一个\r\n if (!primaryApp && appDirs.length > 0) {\r\n primaryApp = appDirs.find((d) => d.startsWith('apps/')) || appDirs[0];\r\n }\r\n\r\n const srcDir = primaryApp ? `${primaryApp}/src` : 'src';\r\n\r\n return {\r\n uiLibrary,\r\n monorepo,\r\n appDirs,\r\n srcDir,\r\n componentDirs: primaryApp\r\n ? [`${primaryApp}/src/views`, `${primaryApp}/src/components`]\r\n : ['src/views', 'src/components'],\r\n pageDirs: primaryApp\r\n ? [`${primaryApp}/src/views`]\r\n : ['src/views'],\r\n componentTestSetup: resolveVbenTestSetup(uiLibrary),\r\n };\r\n },\r\n});\r\n\r\n// ─── 内置框架:Nuxt ────────────────────────────────────────────\r\n\r\nregisterFramework({\r\n type: 'nuxt',\r\n displayName: 'Nuxt',\r\n detect(ctx) {\r\n if (ctx.dependencies['nuxt']) return 1.0;\r\n return 0;\r\n },\r\n resolve(ctx) {\r\n const { cwd, dependencies } = ctx;\r\n const monorepo = detectMonorepo(cwd, ctx.rootFiles);\r\n const uiLibrary = detectUILibrary(dependencies);\r\n\r\n return {\r\n uiLibrary,\r\n monorepo,\r\n appDirs: [],\r\n srcDir: 'src',\r\n componentDirs: ['src/components', 'components', 'src/pages'],\r\n pageDirs: ['src/pages', 'pages'],\r\n componentTestSetup: uiLibrary !== 'none'\r\n ? resolveUITestSetup(uiLibrary)\r\n : undefined,\r\n };\r\n },\r\n});\r\n\r\n// ─── 内置框架:Vue(默认兜底)──────────────────────────────────\r\n\r\nregisterFramework({\r\n type: 'vue',\r\n displayName: 'Vue',\r\n detect(ctx) {\r\n // 只要项目有 vue 依赖就匹配,但置信度最低,作为兜底\r\n if (ctx.dependencies['vue']) return 0.1;\r\n return 0;\r\n },\r\n resolve(ctx) {\r\n const { cwd, dependencies } = ctx;\r\n const monorepo = detectMonorepo(cwd, ctx.rootFiles);\r\n const uiLibrary = detectUILibrary(dependencies);\r\n const appDirs = discoverAppDirs(cwd);\r\n\r\n return {\r\n uiLibrary,\r\n monorepo,\r\n appDirs,\r\n srcDir: 'src',\r\n componentDirs: ['src/components', 'src/views/components', 'src/shared/components'],\r\n pageDirs: ['src/views', 'src/pages'],\r\n componentTestSetup: uiLibrary !== 'none'\r\n ? resolveUITestSetup(uiLibrary)\r\n : undefined,\r\n };\r\n },\r\n});\r\n\r\n// ─── UI 库测试配置解析 ────────────────────────────────────────\r\n\r\nfunction resolveVbenTestSetup(uiLibrary: UILibrary): ComponentTestSetup {\r\n const base = resolveUITestSetup(uiLibrary);\r\n return {\r\n ...base,\r\n extraImports: [\r\n ...(base.extraImports || []),\r\n // Vben 常用的全局注入\r\n `import { createPinia } from 'pinia';`,\r\n ],\r\n globalPlugins: [\r\n ...(base.globalPlugins || []),\r\n 'createPinia()',\r\n ],\r\n };\r\n}\r\n\r\nfunction resolveUITestSetup(uiLibrary: UILibrary): ComponentTestSetup {\r\n switch (uiLibrary) {\r\n case 'ant-design-vue':\r\n return {\r\n extraImports: [`import Antd from 'ant-design-vue';`],\r\n globalPlugins: ['Antd'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [Antd],\r\n },\r\n}`,\r\n };\r\n case 'element-plus':\r\n return {\r\n extraImports: [`import ElementPlus from 'element-plus';`],\r\n globalPlugins: ['ElementPlus'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [ElementPlus],\r\n },\r\n}`,\r\n };\r\n case 'naive-ui':\r\n return {\r\n extraImports: [`import naive from 'naive-ui';`],\r\n globalPlugins: ['naive'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [naive],\r\n },\r\n}`,\r\n };\r\n case 'vuetify':\r\n return {\r\n extraImports: [\r\n `import { createVuetify } from 'vuetify';`,\r\n `import 'vuetify/styles';`,\r\n ],\r\n globalPlugins: ['createVuetify()'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [createVuetify()],\r\n },\r\n}`,\r\n };\r\n case 'primevue':\r\n return {\r\n extraImports: [`import PrimeVue from 'primevue/config';`],\r\n globalPlugins: ['PrimeVue'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [PrimeVue],\r\n },\r\n}`,\r\n };\r\n default:\r\n return {\r\n extraImports: [],\r\n globalPlugins: [],\r\n globalStubs: [],\r\n mountOptions: '{}',\r\n };\r\n }\r\n}\r\n\r\n// ─── 外部扩展加载 ──────────────────────────────────────────────\r\n\r\nimport fs from 'node:fs';\r\nimport { join, extname } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\n\r\n/** 外部扩展文件候选列表(按优先级排序) */\r\nconst EXTERNAL_EXTENSION_FILES = [\r\n 'qat.config.ext.ts',\r\n 'qat.config.ext.js',\r\n 'qat.config.ext.mjs',\r\n 'qat.config.ext.cjs',\r\n];\r\n\r\n/** 外部扩展目录候选 */\r\nconst EXTERNAL_EXTENSION_DIR = 'qat.ext';\r\n\r\n/** 加载结果 */\r\nexport interface LoadExternalFrameworksResult {\r\n /** 成功加载的框架数量 */\r\n loaded: number;\r\n /** 加载的文件列表 */\r\n files: string[];\r\n /** 错误信息 */\r\n errors: { file: string; error: string }[];\r\n}\r\n\r\n/**\r\n * 加载用户项目中的外部扩展文件\r\n *\r\n * 外部扩展文件可以调用以下编程式 API:\r\n * - registerFramework() — 注册自定义框架\r\n * - registerTemplate() — 注册自定义测试模板(需从 qat 导入)\r\n * - registerAIProvider() — 注册自定义 AI Provider(需从 qat 导入)\r\n *\r\n * 扫描规则(按优先级):\r\n * 1. 单文件模式: qat.config.ext.{ts,js,mjs,cjs}\r\n * 2. 目录模式: qat.ext/index.{ts,js,mjs,cjs}\r\n * 3. 目录多文件: qat.ext/*.{ts,js,mjs,cjs} (排除 index)\r\n *\r\n * 安全机制:\r\n * - 静态扫描危险操作(child_process.exec、eval、process.exit 等)\r\n * - 检测到危险操作时跳过加载,除非文件包含 // @qat-security-override\r\n * - 单个文件加载失败不阻断其他文件\r\n *\r\n * @param cwd - 用户项目根目录\r\n * @returns 加载结果统计\r\n */\r\nexport async function loadExternalFrameworks(cwd: string): Promise<LoadExternalFrameworksResult> {\r\n const result: LoadExternalFrameworksResult = {\r\n loaded: 0,\r\n files: [],\r\n errors: [],\r\n };\r\n\r\n // 1. 尝试单文件模式\r\n for (const filename of EXTERNAL_EXTENSION_FILES) {\r\n const filePath = join(cwd, filename);\r\n if (fs.existsSync(filePath)) {\r\n await importAndTrack(filePath, filename, result);\r\n return result; // 找到单文件就返回,不再扫描目录\r\n }\r\n }\r\n\r\n // 2. 尝试目录模式\r\n const dirPath = join(cwd, EXTERNAL_EXTENSION_DIR);\r\n if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {\r\n return result;\r\n }\r\n\r\n // 2a. 尝试 index 入口\r\n const indexNames = ['index.ts', 'index.js', 'index.mjs', 'index.cjs'];\r\n for (const indexName of indexNames) {\r\n const indexPath = join(dirPath, indexName);\r\n if (fs.existsSync(indexPath)) {\r\n await importAndTrack(indexPath, `${EXTERNAL_EXTENSION_DIR}/${indexName}`, result);\r\n return result; // 有 index 就只加载 index\r\n }\r\n }\r\n\r\n // 2b. 加载目录下所有 .ts/.js 文件(按名称排序保证确定性)\r\n const extensions = ['.ts', '.js', '.mjs', '.cjs'];\r\n let entries: string[];\r\n try {\r\n entries = fs.readdirSync(dirPath);\r\n } catch {\r\n return result;\r\n }\r\n\r\n // 排序确保加载顺序稳定\r\n entries.sort();\r\n\r\n for (const entry of entries) {\r\n // 跳过 index(已在上面的逻辑处理)\r\n if (entry.startsWith('index.')) continue;\r\n\r\n const fullPath = join(dirPath, entry);\r\n try {\r\n if (!fs.statSync(fullPath).isFile()) continue;\r\n } catch {\r\n continue;\r\n }\r\n\r\n const ext = extname(entry);\r\n if (!extensions.includes(ext)) continue;\r\n\r\n await importAndTrack(fullPath, `${EXTERNAL_EXTENSION_DIR}/${entry}`, result);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 动态导入单个框架注册文件并跟踪结果\r\n *\r\n * 包含安全检查:扫描文件中的危险操作模式,\r\n * 检测到时跳过加载并记录警告。\r\n */\r\nasync function importAndTrack(\r\n absolutePath: string,\r\n displayPath: string,\r\n result: LoadExternalFrameworksResult,\r\n): Promise<void> {\r\n result.files.push(displayPath);\r\n\r\n try {\r\n // 安全检查:读取文件内容做基本校验\r\n const content = fs.readFileSync(absolutePath, 'utf-8');\r\n\r\n // 允许用户通过注释覆盖安全检查\r\n const hasOverride = content.includes('// @qat-security-override');\r\n\r\n if (!hasOverride) {\r\n // 警告检测:检查是否有明显的危险操作\r\n const dangerPatterns: { pattern: RegExp; label: string }[] = [\r\n { pattern: /child_process\\.(exec|execSync|spawn|spawnSync)\\s*\\(/, label: 'child_process 执行' },\r\n { pattern: /\\beval\\s*\\(/, label: 'eval()' },\r\n { pattern: /require\\s*\\(\\s*['\"]child_process['\"]\\)/, label: 'child_process 引入' },\r\n { pattern: /process\\.exit\\s*\\(/, label: 'process.exit()' },\r\n { pattern: /fs\\.(unlinkSync|rmdirSync|rmSync)\\s*\\(/, label: '危险文件删除操作' },\r\n ];\r\n\r\n for (const { pattern, label } of dangerPatterns) {\r\n if (pattern.test(content)) {\r\n result.errors.push({\r\n file: displayPath,\r\n error: `安全警告: 检测到潜在危险操作 (${label}),已跳过加载。如确认安全,请在文件顶部添加注释 // @qat-security-override`,\r\n });\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // 使用动态 import 加载模块\r\n // 将文件路径转为 file:// URL 以兼容 Windows\r\n const fileUrl = pathToFileURL(absolutePath).href;\r\n await import(fileUrl);\r\n\r\n result.loaded++;\r\n } catch (error) {\r\n const errMsg = error instanceof Error ? error.message : String(error);\r\n result.errors.push({\r\n file: displayPath,\r\n error: errMsg,\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * 重置框架注册表(清空所有已注册的框架)\r\n * 主要用于测试场景\r\n */\r\nexport function resetRegistry(): void {\r\n registry.length = 0;\r\n}\r\n","/**\r\n * 模板引擎 - Handlebars渲染测试用例模板,支持自定义模板注册和Vue版本适配\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { fileURLToPath } from 'node:url';\r\nimport Handlebars from 'handlebars';\r\nimport type { TestType, FrameworkType, UILibrary } from '../types/index.js';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\n/** 导出项信息(简化版,用于模板上下文) */\r\nexport interface TemplateExportInfo {\r\n name: string;\r\n kind: string;\r\n params: string[];\r\n isAsync: boolean;\r\n returnType?: string;\r\n}\r\n\r\n/** Props 信息(简化版,用于模板上下文) */\r\nexport interface TemplatePropInfo {\r\n name: string;\r\n type: string;\r\n required: boolean;\r\n defaultValue?: string;\r\n}\r\n\r\n/** Emits 信息(简化版,用于模板上下文) */\r\nexport interface TemplateEmitInfo {\r\n name: string;\r\n params: string[];\r\n}\r\n\r\n/** 模板变量 */\r\nexport interface TemplateContext {\r\n /** 测试用例名称 */\r\n name: string;\r\n /** camelCase 格式名称(用于 import 变量名) */\r\n camelName?: string;\r\n /** PascalCase 格式名称(用于组件名) */\r\n pascalName?: string;\r\n /** 被测目标文件路径 */\r\n target: string;\r\n /** 前端框架 */\r\n framework: FrameworkType;\r\n /** Vue 主版本 */\r\n vueVersion?: 2 | 3;\r\n /** 是否使用 TypeScript */\r\n typescript?: boolean;\r\n /** UI 组件库 */\r\n uiLibrary?: UILibrary;\r\n /** 额外的导入语句 */\r\n imports?: string[];\r\n /** 组件测试需要的额外导入 */\r\n extraImports?: string[];\r\n /** 组件测试全局插件 */\r\n globalPlugins?: string[];\r\n /** 组件测试全局 stubs */\r\n globalStubs?: string[];\r\n /** mount 配置选项代码片段 */\r\n mountOptions?: string;\r\n /** ── 源码分析结果 ── */\r\n /** 是否有源码分析数据 */\r\n hasAnalysis?: boolean;\r\n /** 导出项列表 */\r\n exports?: TemplateExportInfo[];\r\n /** 函数类型导出 */\r\n functionExports?: TemplateExportInfo[];\r\n /** 非函数类型导出(const/class/component) */\r\n valueExports?: TemplateExportInfo[];\r\n /** Vue 组件 Props */\r\n props?: TemplatePropInfo[];\r\n /** Vue 组件 Emits */\r\n emits?: TemplateEmitInfo[];\r\n /** Vue 组件方法(Options API) */\r\n methods?: string[];\r\n /** Vue 组件 computed(Options API) */\r\n computed?: string[];\r\n /** 是否为 Vue 组件 */\r\n isVueComponent?: boolean;\r\n /** 必填 props */\r\n requiredProps?: TemplatePropInfo[];\r\n /** 有默认值的 props */\r\n optionalProps?: TemplatePropInfo[];\r\n /** 自定义变量 */\r\n [key: string]: unknown;\r\n}\r\n\r\n/** 模板类型到文件名映射 */\r\nconst TEMPLATE_MAP: Record<TestType, string> = {\r\n unit: 'unit-test.hbs',\r\n component: 'component-test.hbs',\r\n e2e: 'e2e-test.hbs',\r\n api: 'api-test.hbs',\r\n visual: 'visual-test.hbs',\r\n performance: 'performance-test.hbs',\r\n};\r\n\r\n/** 自定义模板注册表 */\r\nconst customTemplates = new Map<string, string>();\r\n\r\n// 注册 Handlebars 辅助函数\r\nHandlebars.registerHelper('eq', (a: unknown, b: unknown) => a === b);\r\nHandlebars.registerHelper('neq', (a: unknown, b: unknown) => a !== b);\r\nHandlebars.registerHelper('includes', (arr: unknown[], val: unknown) => Array.isArray(arr) && arr.includes(val));\r\nHandlebars.registerHelper('join', (arr: unknown[], sep: string) => Array.isArray(arr) ? arr.join(sep) : '');\r\nHandlebars.registerHelper('camelCase', (str: string) => toCamelCase(str));\r\nHandlebars.registerHelper('pascalCase', (str: string) => toPascalCase(str));\r\nHandlebars.registerHelper('length', (arr: unknown) => Array.isArray(arr) ? arr.length : 0);\r\nHandlebars.registerHelper('gt', (a: unknown, b: unknown) => Number(a) > Number(b));\r\nHandlebars.registerHelper('and', (...args: unknown[]) => {\r\n args.pop(); // Handlebars options\r\n return args.every(Boolean);\r\n});\r\nHandlebars.registerHelper('or', (...args: unknown[]) => {\r\n args.pop(); // Handlebars options\r\n return args.some(Boolean);\r\n});\r\nHandlebars.registerHelper('propDefaultValue', (prop: TemplatePropInfo) => {\r\n if (!prop.defaultValue) return 'undefined';\r\n const map: Record<string, string> = { String: \"''\", Number: '0', Boolean: 'false', Array: '[]', Object: '{}' };\r\n return map[prop.type] || prop.defaultValue;\r\n});\r\nHandlebars.registerHelper('propTestValue', (prop: TemplatePropInfo) => {\r\n const map: Record<string, string> = {\r\n String: \"'test-value'\",\r\n Number: '42',\r\n Boolean: 'true',\r\n Array: '[1, 2, 3]',\r\n Object: '{ key: \"value\" }',\r\n };\r\n return map[prop.type] || \"'test-value'\";\r\n});\r\n\r\n/**\r\n * 注册自定义模板\r\n * @param type 测试类型\r\n * @param templateContent 模板内容(Handlebars 格式)\r\n */\r\nexport function registerTemplate(type: string, templateContent: string): void {\r\n customTemplates.set(type, templateContent);\r\n}\r\n\r\n/**\r\n * 渲染测试用例模板\r\n */\r\nexport function renderTemplate(type: TestType, context: TemplateContext): string {\r\n const templateContent = loadTemplate(type);\r\n const template = Handlebars.compile(templateContent);\r\n\r\n // 设置默认值(context 中的值优先)\r\n const fullContext: TemplateContext = {\r\n vueVersion: 3,\r\n typescript: true,\r\n imports: [],\r\n extraImports: [],\r\n globalPlugins: [],\r\n globalStubs: [],\r\n mountOptions: '',\r\n hasAnalysis: false,\r\n exports: [],\r\n functionExports: [],\r\n valueExports: [],\r\n props: [],\r\n emits: [],\r\n methods: [],\r\n computed: [],\r\n isVueComponent: false,\r\n requiredProps: [],\r\n optionalProps: [],\r\n ...context,\r\n framework: context.framework || 'vue',\r\n camelName: context.camelName || toCamelCase(context.name),\r\n pascalName: context.pascalName || toPascalCase(context.name),\r\n };\r\n\r\n // 如果有分析数据,派生辅助字段\r\n if (fullContext.exports && fullContext.exports.length > 0) {\r\n fullContext.hasAnalysis = true;\r\n fullContext.functionExports = fullContext.exports.filter(\r\n (e) => e.kind === 'function' || e.kind === 'default',\r\n );\r\n fullContext.valueExports = fullContext.exports.filter(\r\n (e) => e.kind !== 'function' && e.kind !== 'default' && e.kind !== 'type',\r\n );\r\n }\r\n\r\n if (fullContext.props && fullContext.props.length > 0) {\r\n fullContext.isVueComponent = true;\r\n fullContext.requiredProps = fullContext.props.filter((p) => p.required);\r\n fullContext.optionalProps = fullContext.props.filter((p) => !p.required);\r\n }\r\n\r\n if (fullContext.emits && fullContext.emits.length > 0) {\r\n fullContext.isVueComponent = true;\r\n }\r\n\r\n return template(fullContext);\r\n}\r\n\r\n/**\r\n * 加载模板文件\r\n */\r\nfunction loadTemplate(type: TestType): string {\r\n // 优先使用自定义注册的模板\r\n const custom = customTemplates.get(type);\r\n if (custom) return custom;\r\n\r\n // 查找文件系统中的模板\r\n const templateDir = getTemplateDir();\r\n const templateFile = TEMPLATE_MAP[type];\r\n const templatePath = path.join(templateDir, templateFile);\r\n\r\n if (fs.existsSync(templatePath)) {\r\n return fs.readFileSync(templatePath, 'utf-8');\r\n }\r\n\r\n // 使用内置模板\r\n return getBuiltinTemplate(type);\r\n}\r\n\r\n/**\r\n * 获取模板目录\r\n */\r\nfunction getTemplateDir(): string {\r\n // 优先查找项目目录下的 templates\r\n const projectTemplates = path.join(process.cwd(), 'templates');\r\n if (fs.existsSync(projectTemplates)) {\r\n return projectTemplates;\r\n }\r\n // 使用内置模板目录\r\n return path.join(__dirname, '..', 'templates');\r\n}\r\n\r\n/**\r\n * 内置模板 - 适配 Vue 2/3 和 TypeScript\r\n */\r\nfunction getBuiltinTemplate(type: TestType): string {\r\n const templates: Record<TestType, string> = {\r\n unit: `import { describe, it, expect } from 'vitest';\r\n{{#if hasAnalysis}}\r\n{{#each valueExports}}\r\nimport { {{name}} } from '{{../target}}';\r\n{{/each}}\r\n{{#each functionExports}}\r\nimport { {{name}} } from '{{../target}}';\r\n{{/each}}\r\n{{else}}\r\nimport { {{camelName}} } from '{{target}}';\r\n{{/if}}\r\n\r\ndescribe('{{name}}', () => {\r\n{{#if hasAnalysis}}\r\n {{#each functionExports}}\r\n describe('{{name}}()', () => {\r\n {{#if isAsync}}\r\n it('should resolve successfully', async () => {\r\n const result = await {{name}}({{#each params}}{{#unless @first}}, {{/unless}}{{this}}: undefined{{/each}});\r\n expect(result).toBeDefined();\r\n });\r\n {{else}}\r\n it('should return a value', () => {\r\n const result = {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n expect(result).toBeDefined();\r\n });\r\n {{/if}}\r\n {{#if returnType}}\r\n it('should return correct type', () => {\r\n {{#if isAsync}}\r\n const result = await {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n {{else}}\r\n const result = {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n {{/if}}\r\n expect(result).toBeDefined();\r\n });\r\n {{/if}}\r\n {{#if params.length}}\r\n it('should handle edge cases for parameters', () => {\r\n {{#if isAsync}}\r\n const result = await {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n {{else}}\r\n const result = {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n {{/if}}\r\n expect(result).toBeDefined();\r\n });\r\n {{/if}}\r\n });\r\n\r\n {{/each}}\r\n {{#each valueExports}}\r\n describe('{{name}}', () => {\r\n it('should be defined', () => {\r\n expect({{name}}).toBeDefined();\r\n });\r\n {{#if eq kind 'const'}}\r\n it('should have expected value', () => {\r\n // Adjust assertion based on actual value\r\n expect({{name}}).toBeDefined();\r\n });\r\n {{/if}}\r\n {{#if eq kind 'class'}}\r\n it('should be instantiable', () => {\r\n const instance = new {{name}}();\r\n expect(instance).toBeInstanceOf({{name}});\r\n });\r\n {{/if}}\r\n });\r\n\r\n {{/each}}\r\n{{else}}\r\n it('should work correctly', () => {\r\n // TODO: add test logic\r\n expect(true).toBe(true);\r\n });\r\n{{/if}}\r\n});\r\n`,\r\n\r\n component: `import { describe, it, expect } from 'vitest';\r\nimport { mount } from '@vue/test-utils';\r\n{{#each extraImports}}\r\n{{this}}\r\n{{/each}}\r\nimport {{pascalName}} from '{{target}}';\r\n\r\ndescribe('{{name}}', () => {\r\n {{#if isVueComponent}}\r\n const mountComponent = (propsData: Record<string, any> = {}) => {\r\n return mount({{pascalName}}, {\r\n {{#if mountOptions}}\r\n ...{{mountOptions}},\r\n {{/if}}\r\n props: propsData,\r\n });\r\n };\r\n\r\n it('renders correctly', () => {\r\n const wrapper = mountComponent({{#if requiredProps}}{\r\n {{#each requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n {{#if requiredProps}}\r\n it('renders with required props', () => {\r\n const wrapper = mountComponent({\r\n {{#each requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n });\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n {{/if}}\r\n {{#if optionalProps}}\r\n it('renders without optional props', () => {\r\n const wrapper = mountComponent({{#if requiredProps}}{\r\n {{#each requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n it('applies optional props correctly', () => {\r\n const wrapper = mountComponent({\r\n {{#each requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n {{#each optionalProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n });\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n {{/if}}\r\n {{#if emits}}\r\n {{#each emits}}\r\n it('emits {{name}} event', async () => {\r\n const wrapper = mountComponent({{#if ../requiredProps}}{\r\n {{#each ../requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n // TODO: trigger action that emits '{{name}}'\r\n // await wrapper.find('button').trigger('click');\r\n // expect(wrapper.emitted('{{name}}')).toBeTruthy();\r\n });\r\n\r\n {{/each}}\r\n {{/if}}\r\n {{#if computed}}\r\n {{#each computed}}\r\n it('computes {{this}} correctly', () => {\r\n const wrapper = mountComponent({{#if ../requiredProps}}{\r\n {{#each ../requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n // TODO: verify computed property '{{this}}'\r\n // expect(wrapper.vm.{{this}}).toBeDefined();\r\n });\r\n\r\n {{/each}}\r\n {{/if}}\r\n {{#if methods}}\r\n {{#each methods}}\r\n it('calls {{this}} method', async () => {\r\n const wrapper = mountComponent({{#if ../requiredProps}}{\r\n {{#each ../requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n // TODO: test method '{{this}}'\r\n // await wrapper.vm.{{this}}();\r\n // expect(someCondition).toBe(true);\r\n });\r\n\r\n {{/each}}\r\n {{/if}}\r\n {{else}}\r\n it('renders correctly', () => {\r\n const wrapper = mount({{pascalName}}{{#if mountOptions}}, {{mountOptions}}{{/if}});\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n it('renders default slot content', () => {\r\n const wrapper = mount({{pascalName}}, {\r\n {{#if mountOptions}}\r\n ...{{mountOptions}},\r\n {{/if}}\r\n slots: {\r\n default: 'Test Content',\r\n },\r\n });\r\n expect(wrapper.text()).toContain('Test Content');\r\n });\r\n\r\n it('handles user interaction', async () => {\r\n const wrapper = mount({{pascalName}}{{#if mountOptions}}, {{mountOptions}}{{/if}});\r\n // TODO: add interaction test\r\n });\r\n {{/if}}\r\n});\r\n`,\r\n\r\n e2e: `import { test, expect } from '@playwright/test';\r\n\r\ntest.describe('{{name}}', () => {\r\n test.beforeEach(async ({ page }) => {\r\n await page.goto('/');\r\n });\r\n\r\n test('should display page correctly', async ({ page }) => {\r\n // TODO: 添加E2E测试断言\r\n await expect(page).toHaveTitle(/.*/);\r\n });\r\n\r\n test('should navigate between pages', async ({ page }) => {\r\n // TODO: 添加导航测试\r\n });\r\n\r\n test('should be responsive', async ({ page }) => {\r\n await page.setViewportSize({ width: 375, height: 667 });\r\n // TODO: 添加移动端断言\r\n });\r\n});\r\n`,\r\n\r\n api: `import { describe, it, expect, beforeAll, afterAll } from 'vitest';\r\n{{#if imports}}\r\n{{#each imports}}\r\nimport {{this}};\r\n{{/each}}\r\n{{/if}}\r\n\r\ndescribe('{{name}} API', () => {\r\n const baseUrl = process.env.API_BASE_URL || 'http://localhost:3456';\r\n\r\n it('should return 200 for GET request', async () => {\r\n const response = await fetch(\\`\\${baseUrl}/api/{{camelName}}\\`);\r\n expect(response.status).toBe(200);\r\n });\r\n\r\n it('should return expected response format', async () => {\r\n const response = await fetch(\\`\\${baseUrl}/api/{{camelName}}\\`);\r\n const data = await response.json();\r\n expect(data).toBeDefined();\r\n });\r\n\r\n it('should handle error responses', async () => {\r\n const response = await fetch(\\`\\${baseUrl}/api/{{camelName}}/nonexistent\\`, {\r\n method: 'GET',\r\n });\r\n expect(response.status).toBeGreaterThanOrEqual(400);\r\n });\r\n});\r\n`,\r\n\r\n visual: `import { test, expect } from '@playwright/test';\r\n\r\ntest.describe('{{name}} visual regression', () => {\r\n test.beforeEach(async ({ page }) => {\r\n await page.goto('/');\r\n });\r\n\r\n test('should match baseline screenshot - desktop', async ({ page }) => {\r\n await expect(page).toHaveScreenshot('{{name}}-desktop.png');\r\n });\r\n\r\n test('should match baseline screenshot - mobile', async ({ page }) => {\r\n await page.setViewportSize({ width: 375, height: 667 });\r\n await expect(page).toHaveScreenshot('{{name}}-mobile.png');\r\n });\r\n});\r\n`,\r\n\r\n performance: `import { test, expect } from '@playwright/test';\r\n\r\ntest.describe('{{name}} performance', () => {\r\n test('should meet Core Web Vitals thresholds', async ({ page }) => {\r\n await page.goto('/');\r\n\r\n // 测量页面加载性能\r\n const performanceMetrics = await page.evaluate(() => {\r\n const [entry] = performance.getEntriesByType('navigation') as PerformanceNavigationTiming[];\r\n return {\r\n domContentLoaded: entry?.domContentLoadedEventEnd - entry?.domContentLoadedEventStart ?? 0,\r\n loadComplete: entry?.loadEventEnd - entry?.loadEventStart ?? 0,\r\n domInteractive: entry?.domInteractive ?? 0,\r\n };\r\n });\r\n\r\n // DOM 内容加载应在 2s 内\r\n expect(performanceMetrics.domInteractive).toBeLessThan(2000);\r\n });\r\n\r\n test('should not have memory leaks', async ({ page }) => {\r\n await page.goto('/');\r\n\r\n const metrics = await page.metrics();\r\n expect(metrics.JSHeapUsedSize).toBeLessThan(50 * 1024 * 1024); // 50MB\r\n });\r\n});\r\n`,\r\n };\r\n\r\n return templates[type];\r\n}\r\n\r\n/**\r\n * 生成测试文件到磁盘\r\n * @returns 生成的文件路径\r\n */\r\nexport function generateTestFile(\r\n type: TestType,\r\n context: TemplateContext,\r\n outputDir: string,\r\n): string {\r\n const content = renderTemplate(type, context);\r\n\r\n // 根据类型确定输出子目录\r\n const subDirMap: Record<TestType, string> = {\r\n unit: 'unit',\r\n component: 'component',\r\n e2e: 'e2e',\r\n api: 'api',\r\n visual: 'visual',\r\n performance: 'performance',\r\n };\r\n\r\n const subDir = subDirMap[type];\r\n const dir = path.join(outputDir, subDir);\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n // 生成文件名\r\n const fileName = `${context.name}.${type === 'e2e' ? 'spec' : 'test'}.ts`;\r\n const filePath = path.join(dir, fileName);\r\n\r\n // 检查文件是否已存在\r\n if (fs.existsSync(filePath)) {\r\n throw new Error(`测试文件已存在: ${filePath}`);\r\n }\r\n\r\n fs.writeFileSync(filePath, content, 'utf-8');\r\n return filePath;\r\n}\r\n\r\n/**\r\n * Convert string to camelCase\r\n */\r\nfunction toCamelCase(str: string): string {\r\n return str\r\n .replace(/[-_\\s]+(.)?/g, (_, c: string) => (c ? c.toUpperCase() : ''))\r\n .replace(/^[A-Z]/, (c) => c.toLowerCase());\r\n}\r\n\r\n/**\r\n * Convert string to PascalCase\r\n */\r\nfunction toPascalCase(str: string): string {\r\n const camel = toCamelCase(str);\r\n return camel.charAt(0).toUpperCase() + camel.slice(1);\r\n}\r\n","/**\r\n * 报告聚合服务 - 收集各运行器结果,生成 Markdown 报告\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport type { TestRunResult, CoverageResult } from '../types/index.js';\r\n\r\n/** 报告数据 */\r\nexport interface ReportData {\r\n timestamp: number;\r\n duration: number;\r\n results: TestRunResult[];\r\n summary: {\r\n total: number;\r\n passed: number;\r\n failed: number;\r\n skipped: number;\r\n pending: number;\r\n };\r\n /** 按测试类型分组统计 */\r\n byType: Record<string, { total: number; passed: number; failed: number; skipped: number }>;\r\n /** 覆盖率汇总 */\r\n coverage?: CoverageResult;\r\n /** AI 审计报告 */\r\n reviewSummary?: {\r\n total: number;\r\n approved: number;\r\n avgScore: number;\r\n };\r\n}\r\n\r\n/**\r\n * 聚合多个测试运行结果\r\n */\r\nexport function aggregateResults(results: TestRunResult[]): ReportData {\r\n const summary = {\r\n total: 0,\r\n passed: 0,\r\n failed: 0,\r\n skipped: 0,\r\n pending: 0,\r\n };\r\n\r\n const byType: Record<string, { total: number; passed: number; failed: number; skipped: number }> = {};\r\n let coverage: CoverageResult | undefined;\r\n\r\n for (const result of results) {\r\n const typeKey = result.type;\r\n if (!byType[typeKey]) {\r\n byType[typeKey] = { total: 0, passed: 0, failed: 0, skipped: 0 };\r\n }\r\n\r\n for (const suite of result.suites) {\r\n for (const test of suite.tests) {\r\n summary.total++;\r\n byType[typeKey].total++;\r\n\r\n if (test.status === 'passed') {\r\n summary.passed++;\r\n byType[typeKey].passed++;\r\n } else if (test.status === 'failed') {\r\n summary.failed++;\r\n byType[typeKey].failed++;\r\n } else if (test.status === 'skipped') {\r\n summary.skipped++;\r\n byType[typeKey].skipped++;\r\n } else {\r\n summary.pending++;\r\n }\r\n }\r\n }\r\n\r\n // 合并覆盖率\r\n if (result.coverage) {\r\n if (!coverage) {\r\n coverage = { ...result.coverage };\r\n } else {\r\n // 取最大值(多次运行的覆盖率取最好的)\r\n coverage.lines = Math.max(coverage.lines, result.coverage.lines);\r\n coverage.statements = Math.max(coverage.statements, result.coverage.statements);\r\n coverage.functions = Math.max(coverage.functions, result.coverage.functions);\r\n coverage.branches = Math.max(coverage.branches, result.coverage.branches);\r\n }\r\n }\r\n }\r\n\r\n const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);\r\n\r\n return {\r\n timestamp: Date.now(),\r\n duration: totalDuration,\r\n results,\r\n summary,\r\n byType,\r\n coverage: coverage?.lines ? coverage : undefined,\r\n };\r\n}\r\n\r\n/**\r\n * 格式化耗时\r\n */\r\nfunction formatDuration(ms: number): string {\r\n if (ms < 1000) return `${ms}ms`;\r\n if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;\r\n const minutes = Math.floor(ms / 60000);\r\n const seconds = ((ms % 60000) / 1000).toFixed(0);\r\n return `${minutes}m ${seconds}s`;\r\n}\r\n\r\n/**\r\n * 格式化时间戳\r\n */\r\nfunction formatTimestamp(ts: number): string {\r\n return new Date(ts).toLocaleString('zh-CN', {\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit',\r\n });\r\n}\r\n\r\n/**\r\n * 格式化覆盖率为百分比\r\n */\r\nfunction pct(value: number): string {\r\n return `${(value * 100).toFixed(1)}%`;\r\n}\r\n\r\n/**\r\n * 生成覆盖率 Markdown 表格\r\n */\r\nfunction renderCoverageMD(coverage: CoverageResult): string {\r\n return `\r\n### 覆盖率\r\n\r\n| 指标 | 覆盖率 | 进度 |\r\n|------|--------|------|\r\n| 语句 (Statements) | ${pct(coverage.statements)} | ${renderProgressBar(coverage.statements)} |\r\n| 分支 (Branches) | ${pct(coverage.branches)} | ${renderProgressBar(coverage.branches)} |\r\n| 函数 (Functions) | ${pct(coverage.functions)} | ${renderProgressBar(coverage.functions)} |\r\n| 行 (Lines) | ${pct(coverage.lines)} | ${renderProgressBar(coverage.lines)} |\r\n`;\r\n}\r\n\r\n/**\r\n * 渲染简易进度条(文本形式)\r\n */\r\nfunction renderProgressBar(value: number): string {\r\n const filled = Math.round(value * 10);\r\n const empty = 10 - filled;\r\n return `${'█'.repeat(filled)}${'░'.repeat(empty)}`;\r\n}\r\n\r\n/**\r\n * 测试类型的中文标签\r\n */\r\nconst TYPE_LABELS: Record<string, string> = {\r\n unit: '单元测试',\r\n component: '组件测试',\r\n e2e: 'E2E 测试',\r\n api: 'API 测试',\r\n visual: '视觉回归测试',\r\n performance: '性能测试',\r\n};\r\n\r\n/**\r\n * 生成完整 Markdown 报告\r\n */\r\nexport function generateMDReport(data: ReportData): string {\r\n const passRate = data.summary.total > 0\r\n ? ((data.summary.passed / data.summary.total) * 100).toFixed(1)\r\n : '0';\r\n\r\n const rateIcon = parseFloat(passRate) >= 80 ? '✅' : parseFloat(passRate) >= 50 ? '⚠️' : '❌';\r\n\r\n const lines: string[] = [];\r\n\r\n // 标题\r\n lines.push(`# QAT 测试报告`);\r\n lines.push('');\r\n lines.push(`> 生成时间: ${formatTimestamp(data.timestamp)} | 总耗时: ${formatDuration(data.duration)}`);\r\n lines.push('');\r\n\r\n // 总览\r\n lines.push(`## 总览`);\r\n lines.push('');\r\n lines.push(`| 指标 | 数值 |`);\r\n lines.push(`|------|------|`);\r\n lines.push(`| 通过率 | ${rateIcon} **${passRate}%** |`);\r\n lines.push(`| 总用例 | ${data.summary.total} |`);\r\n lines.push(`| ✅ 通过 | ${data.summary.passed} |`);\r\n if (data.summary.failed > 0) lines.push(`| ❌ 失败 | ${data.summary.failed} |`);\r\n if (data.summary.skipped > 0) lines.push(`| ⏭️ 跳过 | ${data.summary.skipped} |`);\r\n if (data.summary.pending > 0) lines.push(`| ⏳ 待定 | ${data.summary.pending} |`);\r\n lines.push(`| ⏱️ 耗时 | ${formatDuration(data.duration)} |`);\r\n lines.push('');\r\n\r\n // 按类型统计\r\n if (Object.keys(data.byType).length > 0) {\r\n lines.push(`## 按类型统计`);\r\n lines.push('');\r\n lines.push(`| 类型 | 通过 | 失败 | 跳过 | 总计 | 通过率 |`);\r\n lines.push(`|------|------|------|------|------|--------|`);\r\n\r\n for (const [type, stats] of Object.entries(data.byType)) {\r\n const label = TYPE_LABELS[type] || type;\r\n const rate = stats.total > 0 ? ((stats.passed / stats.total) * 100).toFixed(0) + '%' : '-';\r\n lines.push(`| ${label} | ${stats.passed} | ${stats.failed} | ${stats.skipped} | ${stats.total} | ${rate} |`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n // 覆盖率\r\n if (data.coverage) {\r\n lines.push(renderCoverageMD(data.coverage));\r\n lines.push('');\r\n }\r\n\r\n // 测试详情\r\n lines.push(`## 测试详情`);\r\n lines.push('');\r\n\r\n for (const result of data.results) {\r\n const typeLabel = TYPE_LABELS[result.type] || result.type;\r\n const statusIcon = result.status === 'passed' ? '✅' : result.status === 'failed' ? '❌' : '⚠️';\r\n\r\n lines.push(`### ${statusIcon} ${typeLabel}`);\r\n lines.push('');\r\n\r\n if (result.suites.length === 0) {\r\n lines.push(`*无测试结果*`);\r\n lines.push('');\r\n continue;\r\n }\r\n\r\n for (const suite of result.suites) {\r\n const suiteIcon = suite.status === 'passed' ? '✅' : suite.status === 'failed' ? '❌' : '⚠️';\r\n lines.push(`#### ${suiteIcon} ${suite.name}`);\r\n lines.push('');\r\n lines.push(`- 文件: \\`${suite.file}\\``);\r\n lines.push(`- 耗时: ${formatDuration(suite.duration)}`);\r\n lines.push('');\r\n\r\n if (suite.tests.length > 0) {\r\n lines.push(`| 状态 | 测试名称 | 耗时 |`);\r\n lines.push(`|------|----------|------|`);\r\n\r\n for (const test of suite.tests) {\r\n const testIcon = test.status === 'passed' ? '✅' : test.status === 'failed' ? '❌' : test.status === 'skipped' ? '⏭️' : '⏳';\r\n const name = test.error ? `**${test.name}**` : test.name;\r\n lines.push(`| ${testIcon} | ${name} | ${formatDuration(test.duration)} |`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n // 失败详情\r\n const failedTests = suite.tests.filter((t) => t.status === 'failed' && t.error);\r\n if (failedTests.length > 0) {\r\n lines.push(`<details>`);\r\n lines.push(`<summary>❌ 失败详情 (${failedTests.length})</summary>`);\r\n lines.push('');\r\n\r\n for (const test of failedTests) {\r\n lines.push(`**${test.name}**`);\r\n lines.push('```');\r\n lines.push(test.error!.message);\r\n if (test.error!.stack) {\r\n lines.push(test.error!.stack);\r\n }\r\n if (test.error!.expected && test.error!.actual) {\r\n lines.push(`Expected: ${test.error!.expected}`);\r\n lines.push(`Actual: ${test.error!.actual}`);\r\n }\r\n lines.push('```');\r\n lines.push('');\r\n }\r\n\r\n lines.push(`</details>`);\r\n lines.push('');\r\n }\r\n }\r\n }\r\n\r\n // 页脚\r\n lines.push('---');\r\n lines.push('');\r\n lines.push(`*由 QAT 自动化测试工具生成 | ${formatTimestamp(data.timestamp)}*`);\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n/**\r\n * 将报告写入磁盘(Markdown 格式)\r\n * @returns 报告文件路径\r\n */\r\nexport function writeReportToDisk(data: ReportData, outputDir: string): string {\r\n const md = generateMDReport(data);\r\n const dir = path.resolve(outputDir);\r\n\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n const mdPath = path.join(dir, 'report.md');\r\n fs.writeFileSync(mdPath, md, 'utf-8');\r\n\r\n // 同时写入 JSON 格式的原始数据,供程序读取\r\n const jsonPath = path.join(dir, 'report.json');\r\n fs.writeFileSync(jsonPath, JSON.stringify(data, null, 2), 'utf-8');\r\n\r\n return mdPath;\r\n}\r\n\r\n/**\r\n * 生成 HTML 报告(保留兼容,从 Markdown 转换为简易 HTML)\r\n */\r\nexport function generateHTMLReport(data: ReportData): string {\r\n const passRate = data.summary.total > 0\r\n ? ((data.summary.passed / data.summary.total) * 100).toFixed(1)\r\n : '0';\r\n\r\n const md = generateMDReport(data);\r\n\r\n // 简易 Markdown → HTML 转换\r\n const html = md\r\n .replace(/^### (.+)$/gm, '<h3>$1</h3>')\r\n .replace(/^## (.+)$/gm, '<h2>$1</h2>')\r\n .replace(/^# (.+)$/gm, '<h1>$1</h1>')\r\n .replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>')\r\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\r\n .replace(/`([^`]+)`/g, '<code>$1</code>')\r\n .replace(/^---$/gm, '<hr>')\r\n .replace(/\\|(.+)\\|/g, (match) => {\r\n const cells = match.split('|').filter(Boolean).map((c: string) => c.trim());\r\n if (cells.every((c: string) => c.startsWith('-') || c === '')) return '';\r\n const tds = cells.map((c: string) => `<td>${c}</td>`).join('');\r\n return `<tr>${tds}</tr>`;\r\n })\r\n .replace(/<tr>/g, '<table><tr>')\r\n .replace(/<\\/tr>(?!\\s*<table>)/g, '</tr></table>')\r\n .replace(/<\\/table>\\s*<table>/g, '')\r\n .replace(/^✅/gm, '<span style=\"color:#22c55e\">✅</span>')\r\n .replace(/^❌/gm, '<span style=\"color:#ef4444\">❌</span>')\r\n .replace(/^⚠️/gm, '<span style=\"color:#f59e0b\">⚠️</span>');\r\n\r\n return `<!DOCTYPE html>\r\n<html lang=\"zh-CN\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>QAT 测试报告 - ${formatTimestamp(data.timestamp)}</title>\r\n <style>\r\n body {\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\r\n max-width: 960px; margin: 0 auto; padding: 20px;\r\n background: #f8fafc; color: #1e293b; line-height: 1.6;\r\n }\r\n h1 { border-bottom: 2px solid #3b82f6; padding-bottom: 8px; }\r\n h2 { margin-top: 24px; color: #1e40af; }\r\n h3 { margin-top: 16px; color: #334155; }\r\n h4 { margin-top: 12px; color: #475569; }\r\n blockquote { color: #64748b; border-left: 3px solid #cbd5e1; padding-left: 12px; margin: 8px 0; }\r\n table { border-collapse: collapse; width: 100%; margin: 12px 0; }\r\n td, th { border: 1px solid #e2e8f0; padding: 8px 12px; text-align: left; }\r\n tr:nth-child(even) { background: #f1f5f9; }\r\n code { background: #e2e8f0; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }\r\n pre { background: #1e293b; color: #e2e8f0; padding: 16px; border-radius: 8px; overflow-x: auto; }\r\n pre code { background: none; padding: 0; }\r\n hr { border: none; border-top: 1px solid #e2e8f0; margin: 24px 0; }\r\n details { margin: 8px 0; }\r\n summary { cursor: pointer; color: #ef4444; font-weight: 600; }\r\n strong { color: #1e293b; }\r\n .pass-rate { font-size: 48px; font-weight: 700; text-align: center; margin: 20px 0;\r\n color: ${parseFloat(passRate) >= 80 ? '#22c55e' : parseFloat(passRate) >= 50 ? '#f59e0b' : '#ef4444'};\r\n }\r\n .pass-rate-label { text-align: center; color: #64748b; margin-bottom: 20px; }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"pass-rate\">${passRate}%</div>\r\n <div class=\"pass-rate-label\">测试通过率</div>\r\n ${html}\r\n</body>\r\n</html>`;\r\n}\r\n","/**\r\n * Vitest 运行器 - 通过子进程调用Vitest,解析输出收集结果\r\n */\r\n\r\nimport { execFile } from 'node:child_process';\r\nimport path from 'node:path';\r\nimport type { TestRunResult, TestSuiteResult, TestCaseResult, TestStatus, TestType, CoverageResult } from '../types/index.js';\r\n\r\nexport interface VitestRunnerOptions {\r\n type: TestType;\r\n files?: string[];\r\n watch?: boolean;\r\n coverage?: boolean;\r\n configPath?: string;\r\n /** 额外传递给 vitest 的参数 */\r\n extraArgs?: string[];\r\n}\r\n\r\n/**\r\n * 执行Vitest测试\r\n */\r\nexport async function runVitest(options: VitestRunnerOptions): Promise<TestRunResult> {\r\n const startTime = Date.now();\r\n\r\n const args = buildVitestArgs(options);\r\n\r\n try {\r\n const result = await execVitest(args);\r\n const endTime = Date.now();\r\n\r\n return {\r\n type: options.type,\r\n status: result.success ? 'passed' : 'failed',\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites: result.suites,\r\n coverage: result.coverage,\r\n };\r\n } catch (error) {\r\n const endTime = Date.now();\r\n return {\r\n type: options.type,\r\n status: 'failed',\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites: [{\r\n name: 'Vitest Runner',\r\n file: options.files?.[0] || 'unknown',\r\n type: options.type,\r\n status: 'failed',\r\n duration: endTime - startTime,\r\n tests: [{\r\n name: 'Runner Error',\r\n file: options.files?.[0] || 'unknown',\r\n status: 'failed',\r\n duration: 0,\r\n error: {\r\n message: error instanceof Error ? error.message : String(error),\r\n },\r\n retries: 0,\r\n }],\r\n }],\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * 构建vitest命令参数\r\n */\r\nfunction buildVitestArgs(options: VitestRunnerOptions): string[] {\r\n const args = ['vitest', 'run', '--reporter=json'];\r\n\r\n // 指定测试文件\r\n if (options.files && options.files.length > 0) {\r\n args.push(...options.files);\r\n } else {\r\n // 根据测试类型确定包含路径\r\n const includeMap: Record<string, string[]> = {\r\n unit: ['tests/unit'],\r\n component: ['tests/component'],\r\n api: ['tests/api'],\r\n };\r\n const includes = includeMap[options.type];\r\n if (includes) {\r\n args.push('--include', includes.map((d) => `${d}/**/*.test.ts`).join(','));\r\n }\r\n }\r\n\r\n // 覆盖率\r\n if (options.coverage) {\r\n args.push('--coverage');\r\n }\r\n\r\n // 配置文件\r\n if (options.configPath) {\r\n args.push('--config', options.configPath);\r\n }\r\n\r\n // 额外参数\r\n if (options.extraArgs) {\r\n args.push(...options.extraArgs);\r\n }\r\n\r\n return args;\r\n}\r\n\r\n/**\r\n * 执行vitest命令并解析JSON输出\r\n * 使用 --outputFile 将结果写入临时文件,避免 stdout 混杂日志导致 JSON 解析失败\r\n */\r\nasync function execVitest(args: string[]): Promise<{\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n coverage?: CoverageResult;\r\n rawOutput: string;\r\n}> {\r\n const os = await import('node:os');\r\n const fs = await import('node:fs');\r\n const tmpFile = path.join(os.tmpdir(), `qat-vitest-result-${Date.now()}.json`);\r\n\r\n // 添加 outputFile 参数让 vitest 将 JSON 结果写入临时文件\r\n const argsWithOutput = [...args, '--outputFile', tmpFile];\r\n\r\n return new Promise((resolve, reject) => {\r\n const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n const child = execFile(npx, argsWithOutput, {\r\n cwd: process.cwd(),\r\n env: { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1' },\r\n maxBuffer: 50 * 1024 * 1024,\r\n shell: true,\r\n }, (error, stdout, stderr) => {\r\n const rawOutput = stdout || stderr || '';\r\n\r\n // 优先从临时文件读取 JSON 结果\r\n let jsonResult: string | null = null;\r\n try {\r\n if (fs.existsSync(tmpFile)) {\r\n jsonResult = fs.readFileSync(tmpFile, 'utf-8');\r\n fs.unlinkSync(tmpFile); // 读取后删除临时文件\r\n }\r\n } catch {\r\n // 临时文件读取失败\r\n }\r\n\r\n // 解析 JSON\r\n if (jsonResult) {\r\n try {\r\n const parsed = parseVitestJSONResult(jsonResult);\r\n resolve({ ...parsed, rawOutput });\r\n return;\r\n } catch {\r\n // JSON 解析失败,回退到文本解析\r\n }\r\n }\r\n\r\n // 回退:从 stdout 尝试提取 JSON\r\n try {\r\n const parsed = parseVitestJSONOutput(rawOutput);\r\n resolve({ ...parsed, rawOutput });\r\n } catch {\r\n // 最终回退:从文本输出提取结果\r\n if (rawOutput) {\r\n resolve({ ...parseVitestTextOutput(rawOutput, !!error), rawOutput });\r\n } else if (error && error.message.includes('ENOENT')) {\r\n reject(new Error('未找到 vitest,请确保已安装: npm install -D vitest'));\r\n } else {\r\n resolve({ success: !error, suites: [], rawOutput });\r\n }\r\n }\r\n });\r\n\r\n child.on('error', (err) => {\r\n // 清理临时文件\r\n try { fs.unlinkSync(tmpFile); } catch {}\r\n reject(new Error(`Vitest 执行失败: ${err.message}`));\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 解析 vitest 标准的 JSON 输出(从文件读取的纯净 JSON)\r\n * Vitest JSON reporter 格式参考: https://vitest.dev/config/#reporters\r\n */\r\nfunction parseVitestJSONResult(jsonStr: string): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n coverage?: CoverageResult;\r\n} {\r\n const data = JSON.parse(jsonStr);\r\n const suites: TestSuiteResult[] = [];\r\n\r\n // Vitest JSON 格式: { numTotalTests, numPassedTests, testResults: [...] }\r\n if (data.testResults && Array.isArray(data.testResults)) {\r\n for (const fileResult of data.testResults) {\r\n const suiteTests: TestCaseResult[] = [];\r\n\r\n // assertionResults 包含每个测试用例\r\n const assertions = fileResult.assertionResults || fileResult.tests || [];\r\n for (const assertion of assertions) {\r\n suiteTests.push({\r\n name: assertion.title || assertion.fullName || assertion.name || 'unknown',\r\n file: fileResult.name || 'unknown',\r\n status: mapVitestStatus(assertion.status as string),\r\n duration: (assertion.duration as number) || 0,\r\n error: (assertion.failureMessages as string[] | undefined)?.length\r\n ? { message: (assertion.failureMessages as string[])[0] }\r\n : assertion.failureMessage\r\n ? { message: assertion.failureMessage as string }\r\n : undefined,\r\n retries: 0,\r\n });\r\n }\r\n\r\n // 如果没有 assertionResults 但有 numPassingTests 等汇总,构造简要结果\r\n if (suiteTests.length === 0 && fileResult.numPassingTests !== undefined) {\r\n const counts = [\r\n { n: fileResult.numPassingTests || 0, s: 'passed' as TestStatus },\r\n { n: fileResult.numFailingTests || 0, s: 'failed' as TestStatus },\r\n { n: fileResult.numPendingTests || 0, s: 'skipped' as TestStatus },\r\n ];\r\n for (const { n, s } of counts) {\r\n for (let i = 0; i < n; i++) {\r\n suiteTests.push({\r\n name: `${s} test ${i + 1}`,\r\n file: fileResult.name || 'unknown',\r\n status: s,\r\n duration: 0,\r\n retries: 0,\r\n });\r\n }\r\n }\r\n }\r\n\r\n suites.push({\r\n name: path.basename(fileResult.name || 'unknown'),\r\n file: fileResult.name || 'unknown',\r\n type: 'unit' as TestType,\r\n status: mapVitestStatus(fileResult.status),\r\n duration: fileResult.duration || 0,\r\n tests: suiteTests,\r\n });\r\n }\r\n }\r\n\r\n // 解析覆盖率(Vitest coverage 输出格式)\r\n let coverage: CoverageResult | undefined;\r\n if (data.coverageMap) {\r\n coverage = extractCoverage(data.coverageMap);\r\n }\r\n\r\n const success = data.success !== false && data.numFailedTests === undefined\r\n ? suites.every((s) => s.status !== 'failed')\r\n : (data.numFailedTests || 0) === 0;\r\n\r\n return { success, suites, coverage };\r\n}\r\n\r\n/**\r\n * 从 stdout 中尝试提取并解析 vitest JSON 输出(回退方案)\r\n */\r\nfunction parseVitestJSONOutput(output: string): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n coverage?: CoverageResult;\r\n} {\r\n // vitest --reporter=json 输出 JSON 结果\r\n // 尝试从输出中提取 JSON\r\n const jsonMatch = output.match(/\\{[\\s\\S]*\"testResults\"[\\s\\S]*\\}/);\r\n\r\n if (!jsonMatch) {\r\n return parseVitestTextOutput(output, false);\r\n }\r\n\r\n try {\r\n const data = JSON.parse(jsonMatch[0]);\r\n const suites: TestSuiteResult[] = [];\r\n\r\n // vitest JSON reporter 格式\r\n if (data.testResults && Array.isArray(data.testResults)) {\r\n for (const fileResult of data.testResults) {\r\n const suite: TestSuiteResult = {\r\n name: path.basename(fileResult.name || fileResult.assertionResults?.[0]?.ancestorTitles?.[0] || 'unknown'),\r\n file: fileResult.name || 'unknown',\r\n type: 'unit' as TestType,\r\n status: mapVitestStatus(fileResult.status),\r\n duration: fileResult.duration || 0,\r\n tests: (fileResult.assertionResults || []).map((assertion: Record<string, unknown>) => ({\r\n name: assertion.title || assertion.fullName || 'unknown',\r\n file: fileResult.name || 'unknown',\r\n status: mapVitestStatus(assertion.status as string),\r\n duration: (assertion.duration as number) || 0,\r\n error: (assertion.failureMessages as string[] | undefined)?.length\r\n ? { message: ((assertion.failureMessages as string[])[0]) }\r\n : undefined,\r\n retries: 0,\r\n }) as TestCaseResult),\r\n };\r\n suites.push(suite);\r\n }\r\n }\r\n\r\n // 解析覆盖率\r\n let coverage: CoverageResult | undefined;\r\n if (data.coverageMap) {\r\n coverage = extractCoverage(data.coverageMap);\r\n }\r\n\r\n const success = data.success !== false && suites.every((s) => s.status !== 'failed');\r\n\r\n return { success, suites, coverage };\r\n } catch {\r\n return parseVitestTextOutput(output, false);\r\n }\r\n}\r\n\r\n/**\r\n * 解析 vitest 文本输出(JSON 解析失败时的回退方案)\r\n */\r\nfunction parseVitestTextOutput(output: string, hasError: boolean): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n} {\r\n const suites: TestSuiteResult[] = [];\r\n let totalPassed = 0;\r\n let totalFailed = 0;\r\n\r\n // 匹配 vitest 文本输出格式\r\n // ✓ tests/unit/example.test.ts (2 tests)\r\n const suiteRegex = /[✓✗×]\\s+(.+\\.test\\.ts|.+\\.spec\\.ts)\\s*\\((\\d+)\\s+test/i;\r\n const lines = output.split('\\n');\r\n\r\n for (const line of lines) {\r\n const match = line.match(suiteRegex);\r\n if (match) {\r\n const file = match[1];\r\n const testCount = parseInt(match[2], 10);\r\n const isPassed = line.includes('✓');\r\n\r\n if (isPassed) totalPassed += testCount;\r\n else totalFailed += testCount;\r\n\r\n suites.push({\r\n name: path.basename(file),\r\n file,\r\n type: 'unit',\r\n status: isPassed ? 'passed' : 'failed',\r\n duration: 0,\r\n tests: Array.from({ length: testCount }, (_, i) => ({\r\n name: `test ${i + 1}`,\r\n file,\r\n status: isPassed ? ('passed' as TestStatus) : ('failed' as TestStatus),\r\n duration: 0,\r\n retries: 0,\r\n })),\r\n });\r\n }\r\n }\r\n\r\n return {\r\n success: !hasError || totalFailed === 0,\r\n suites,\r\n };\r\n}\r\n\r\n/**\r\n * 映射 vitest 状态到 QAT 状态\r\n */\r\nfunction mapVitestStatus(status: string): TestStatus {\r\n switch (status) {\r\n case 'passed':\r\n case 'pass':\r\n return 'passed';\r\n case 'failed':\r\n case 'fail':\r\n return 'failed';\r\n case 'skipped':\r\n case 'skip':\r\n case 'pending':\r\n return 'skipped';\r\n default:\r\n return 'pending';\r\n }\r\n}\r\n\r\n/**\r\n * 从 vitest coverage map 提取汇总数据\r\n */\r\nfunction extractCoverage(coverageMap: Record<string, unknown>): CoverageResult {\r\n let totalStatements = 0;\r\n let coveredStatements = 0;\r\n let totalFunctions = 0;\r\n let coveredFunctions = 0;\r\n let totalBranches = 0;\r\n let coveredBranches = 0;\r\n let totalLines = 0;\r\n let coveredLines = 0;\r\n\r\n for (const fileCov of Object.values(coverageMap) as Record<string, unknown>[]) {\r\n const s = fileCov.s as Record<string, number> || {};\r\n const f = fileCov.f as Record<string, number> || {};\r\n const b = fileCov.b as Record<string, Record<string, number>> || {};\r\n\r\n for (const count of Object.values(s)) {\r\n totalLines++;\r\n if (count > 0) coveredLines++;\r\n }\r\n\r\n for (const count of Object.values(f)) {\r\n totalFunctions++;\r\n if (count > 0) coveredFunctions++;\r\n }\r\n\r\n for (const branch of Object.values(b)) {\r\n for (const count of Object.values(branch)) {\r\n totalBranches++;\r\n if (count > 0) coveredBranches++;\r\n }\r\n }\r\n }\r\n\r\n totalStatements = totalLines;\r\n coveredStatements = coveredLines;\r\n\r\n return {\r\n lines: totalLines > 0 ? coveredLines / totalLines : 0,\r\n statements: totalStatements > 0 ? coveredStatements / totalStatements : 0,\r\n functions: totalFunctions > 0 ? coveredFunctions / totalFunctions : 0,\r\n branches: totalBranches > 0 ? coveredBranches / totalBranches : 0,\r\n };\r\n}\r\n","/**\r\n * Playwright 运行器 - 通过子进程调用Playwright,解析输出收集结果\r\n */\r\n\r\nimport { execFile } from 'node:child_process';\r\nimport path from 'node:path';\r\nimport type { TestRunResult, TestSuiteResult, TestCaseResult, TestStatus, TestType } from '../types/index.js';\r\n\r\nexport interface PlaywrightRunnerOptions {\r\n type: TestType;\r\n files?: string[];\r\n browsers?: ('chromium' | 'firefox' | 'webkit')[];\r\n headed?: boolean;\r\n configPath?: string;\r\n /** 额外传递给 playwright 的参数 */\r\n extraArgs?: string[];\r\n}\r\n\r\n/**\r\n * 执行Playwright测试\r\n */\r\nexport async function runPlaywright(options: PlaywrightRunnerOptions): Promise<TestRunResult> {\r\n const startTime = Date.now();\r\n\r\n const args = buildPlaywrightArgs(options);\r\n\r\n try {\r\n const result = await execPlaywright(args);\r\n const endTime = Date.now();\r\n\r\n return {\r\n type: options.type,\r\n status: result.success ? 'passed' : 'failed',\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites: result.suites,\r\n };\r\n } catch (error) {\r\n const endTime = Date.now();\r\n return {\r\n type: options.type,\r\n status: 'failed',\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites: [{\r\n name: 'Playwright Runner',\r\n file: options.files?.[0] || 'unknown',\r\n type: options.type,\r\n status: 'failed',\r\n duration: endTime - startTime,\r\n tests: [{\r\n name: 'Runner Error',\r\n file: options.files?.[0] || 'unknown',\r\n status: 'failed',\r\n duration: 0,\r\n error: {\r\n message: error instanceof Error ? error.message : String(error),\r\n },\r\n retries: 0,\r\n }],\r\n }],\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * 构建playwright命令参数\r\n */\r\nfunction buildPlaywrightArgs(options: PlaywrightRunnerOptions): string[] {\r\n const args = ['playwright', 'test', '--reporter=json'];\r\n\r\n // 指定测试文件或目录\r\n if (options.files && options.files.length > 0) {\r\n args.push(...options.files);\r\n } else {\r\n // 根据测试类型确定测试目录\r\n const dirMap: Record<string, string> = {\r\n e2e: 'tests/e2e',\r\n visual: 'tests/visual',\r\n performance: 'tests/e2e',\r\n };\r\n const testDir = dirMap[options.type];\r\n if (testDir) {\r\n args.push(testDir);\r\n }\r\n }\r\n\r\n // 指定浏览器\r\n if (options.browsers && options.browsers.length > 0) {\r\n for (const browser of options.browsers) {\r\n args.push('--project', browser);\r\n }\r\n }\r\n\r\n // 有头模式\r\n if (options.headed) {\r\n args.push('--headed');\r\n }\r\n\r\n // 配置文件\r\n if (options.configPath) {\r\n args.push('--config', options.configPath);\r\n }\r\n\r\n // 额外参数\r\n if (options.extraArgs) {\r\n args.push(...options.extraArgs);\r\n }\r\n\r\n return args;\r\n}\r\n\r\n/**\r\n * 执行playwright命令并解析JSON输出\r\n */\r\nasync function execPlaywright(args: string[]): Promise<{\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n}> {\r\n return new Promise((resolve, reject) => {\r\n const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n const child = execFile(npx, args, {\r\n cwd: process.cwd(),\r\n env: { ...process.env, FORCE_COLOR: '0', PLAYWRIGHT_JSON_OUTPUT_DIR: '' },\r\n maxBuffer: 50 * 1024 * 1024,\r\n shell: true,\r\n }, (error, stdout, stderr) => {\r\n const output = stdout || stderr || '';\r\n\r\n try {\r\n const parsed = parsePlaywrightJSONOutput(output);\r\n resolve(parsed);\r\n } catch {\r\n if (output) {\r\n resolve(parsePlaywrightTextOutput(output, !!error));\r\n } else if (error && error.message.includes('ENOENT')) {\r\n reject(new Error('未找到 playwright,请确保已安装: npm install -D @playwright/test'));\r\n } else {\r\n resolve({ success: !error, suites: [] });\r\n }\r\n }\r\n });\r\n\r\n child.on('error', (err) => {\r\n reject(new Error(`Playwright 执行失败: ${err.message}`));\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 解析 Playwright JSON 输出\r\n */\r\nfunction parsePlaywrightJSONOutput(output: string): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n} {\r\n // Playwright JSON reporter 输出格式\r\n // 尝试提取 JSON\r\n const jsonMatch = output.match(/\\{[\\s\\S]*\"suites\"[\\s\\S]*\\}/);\r\n\r\n if (!jsonMatch) {\r\n return parsePlaywrightTextOutput(output, false);\r\n }\r\n\r\n try {\r\n const data = JSON.parse(jsonMatch[0]);\r\n const suites: TestSuiteResult[] = [];\r\n\r\n // Playwright JSON reporter 格式\r\n if (data.suites && Array.isArray(data.suites)) {\r\n for (const suiteData of data.suites) {\r\n extractPlaywrightSuites(suiteData, suites);\r\n }\r\n }\r\n\r\n const success = data.stats?.status === 'passed' || suites.every((s) => s.status !== 'failed');\r\n\r\n return { success, suites };\r\n } catch {\r\n return parsePlaywrightTextOutput(output, false);\r\n }\r\n}\r\n\r\n/**\r\n * 递归提取 Playwright 套件\r\n */\r\nfunction extractPlaywrightSuites(\r\n suiteData: Record<string, unknown>,\r\n result: TestSuiteResult[],\r\n parentPath = '',\r\n): void {\r\n const suiteName = (suiteData.title as string) || 'unknown';\r\n const suitePath = parentPath ? `${parentPath} > ${suiteName}` : suiteName;\r\n\r\n // 如果有 specs,这是一个叶子套件\r\n if (suiteData.specs && Array.isArray(suiteData.specs)) {\r\n const tests: TestCaseResult[] = (suiteData.specs as Record<string, unknown>[]).map((spec) => {\r\n const specTitle = (spec.title as string) || 'unknown';\r\n const specFile = (spec.file as string) || 'unknown';\r\n // 从 tests 数组获取状态\r\n const specTests = spec.tests as Record<string, unknown>[] | undefined;\r\n let status: TestStatus = 'pending';\r\n let duration = 0;\r\n let error: { message: string; stack?: string } | undefined;\r\n\r\n if (specTests && specTests.length > 0) {\r\n const lastRun = specTests[specTests.length - 1] as Record<string, unknown>;\r\n const results = lastRun.results as Record<string, unknown>[] | undefined;\r\n if (results && results.length > 0) {\r\n const lastResult = results[results.length - 1] as Record<string, unknown>;\r\n status = mapPlaywrightStatus(lastResult.status as string);\r\n duration = (lastResult.duration as number) || 0;\r\n if (lastResult.error) {\r\n const err = lastResult.error as Record<string, unknown>;\r\n error = {\r\n message: (err.message as string) || '',\r\n stack: err.stack as string | undefined,\r\n };\r\n }\r\n }\r\n }\r\n\r\n return {\r\n name: specTitle,\r\n file: specFile,\r\n status,\r\n duration,\r\n error,\r\n retries: 0,\r\n };\r\n });\r\n\r\n const suiteStatus = tests.some((t) => t.status === 'failed')\r\n ? 'failed'\r\n : tests.every((t) => t.status === 'skipped')\r\n ? 'skipped'\r\n : 'passed';\r\n\r\n result.push({\r\n name: suiteName,\r\n file: ((suiteData.specs as Record<string, unknown>[])[0]?.file as string) || 'unknown',\r\n type: 'e2e',\r\n status: suiteStatus,\r\n duration: tests.reduce((sum, t) => sum + t.duration, 0),\r\n tests,\r\n });\r\n }\r\n\r\n // 递归处理子套件\r\n if (suiteData.suites && Array.isArray(suiteData.suites)) {\r\n for (const child of suiteData.suites as Record<string, unknown>[]) {\r\n extractPlaywrightSuites(child, result, suitePath);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 解析 Playwright 文本输出\r\n */\r\nfunction parsePlaywrightTextOutput(output: string, hasError: boolean): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n} {\r\n const suites: TestSuiteResult[] = [];\r\n let totalPassed = 0;\r\n let totalFailed = 0;\r\n let totalSkipped = 0;\r\n\r\n // 匹配 Playwright 文本输出\r\n // ✓ 1 test.ts:3:1 › login page › should display login form (2s)\r\n // ✘ 2 test.ts:10:1 › login page › should submit form (1s)\r\n // - 3 test.ts:20:1 › login page › should show error (skipped)\r\n const testRegex = /^\\s*([✓✘×\\-])\\s+\\d+\\s+(.+\\.test\\.ts|.+\\.spec\\.ts)\\s*:\\d+:\\d+\\s*›\\s*(.+?)\\s*\\((\\d+)ms?\\)/;\r\n\r\n for (const line of output.split('\\n')) {\r\n const match = line.match(testRegex);\r\n if (match) {\r\n const symbol = match[1];\r\n const file = match[2];\r\n const testName = match[3];\r\n const duration = parseInt(match[4], 10);\r\n\r\n let status: TestStatus;\r\n if (symbol === '✓') {\r\n status = 'passed';\r\n totalPassed++;\r\n } else if (symbol === '✘' || symbol === '×') {\r\n status = 'failed';\r\n totalFailed++;\r\n } else {\r\n status = 'skipped';\r\n totalSkipped++;\r\n }\r\n\r\n // 按文件分组\r\n let existingSuite = suites.find((s) => s.file === file);\r\n if (!existingSuite) {\r\n existingSuite = {\r\n name: path.basename(file),\r\n file,\r\n type: 'e2e',\r\n status: 'passed',\r\n duration: 0,\r\n tests: [],\r\n };\r\n suites.push(existingSuite);\r\n }\r\n\r\n existingSuite.tests.push({\r\n name: testName,\r\n file,\r\n status,\r\n duration,\r\n retries: 0,\r\n });\r\n\r\n if (status === 'failed') {\r\n existingSuite.status = 'failed';\r\n }\r\n }\r\n }\r\n\r\n // 更新套件状态和持续时间\r\n for (const suite of suites) {\r\n if (suite.tests.some((t) => t.status === 'failed')) {\r\n suite.status = 'failed';\r\n }\r\n suite.duration = suite.tests.reduce((sum, t) => sum + t.duration, 0);\r\n }\r\n\r\n // 匹配最终汇总\r\n // 3 passed, 1 failed, 1 skipped\r\n const summaryRegex = /(\\d+)\\s+passed.*?(\\d+)\\s+failed.*?(\\d+)\\s+skipped/i;\r\n const summaryMatch = output.match(summaryRegex);\r\n if (summaryMatch && suites.length === 0) {\r\n // 从汇总推断\r\n totalPassed = parseInt(summaryMatch[1], 10);\r\n totalFailed = parseInt(summaryMatch[2], 10);\r\n totalSkipped = parseInt(summaryMatch[3], 10);\r\n }\r\n\r\n return {\r\n success: !hasError || totalFailed === 0,\r\n suites,\r\n };\r\n}\r\n\r\n/**\r\n * 映射 Playwright 状态\r\n */\r\nfunction mapPlaywrightStatus(status: string): TestStatus {\r\n switch (status) {\r\n case 'passed':\r\n case 'pass':\r\n return 'passed';\r\n case 'failed':\r\n case 'fail':\r\n case 'timedOut':\r\n case 'interrupted':\r\n return 'failed';\r\n case 'skipped':\r\n case 'skip':\r\n return 'skipped';\r\n default:\r\n return 'pending';\r\n }\r\n}\r\n","/**\r\n * Lighthouse 运行器 - 通过子进程调用Lighthouse,提取性能指标\r\n */\r\n\r\nimport { execFile } from 'node:child_process';\r\nimport type { TestRunResult, TestSuiteResult, TestCaseResult, TestStatus, PerformanceMetrics } from '../types/index.js';\r\n\r\nexport interface LighthouseRunnerOptions {\r\n urls: string[];\r\n runs?: number;\r\n thresholds?: Partial<Record<'performance' | 'accessibility' | 'best-practices' | 'seo', number>>;\r\n /** 额外传递给 lighthouse 的参数 */\r\n extraArgs?: string[];\r\n}\r\n\r\n/**\r\n * 执行Lighthouse性能测试\r\n */\r\nexport async function runLighthouse(options: LighthouseRunnerOptions): Promise<TestRunResult> {\r\n const startTime = Date.now();\r\n const runs = options.runs || 3;\r\n const urls = options.urls;\r\n\r\n if (urls.length === 0) {\r\n return {\r\n type: 'performance',\r\n status: 'skipped',\r\n startTime,\r\n endTime: Date.now(),\r\n duration: 0,\r\n suites: [],\r\n };\r\n }\r\n\r\n const allResults: LighthouseRunResult[] = [];\r\n\r\n for (const url of urls) {\r\n try {\r\n const urlResults: LighthouseRunResult[] = [];\r\n\r\n for (let i = 0; i < runs; i++) {\r\n const result = await runLighthouseOnce(url, options.extraArgs);\r\n urlResults.push(result);\r\n }\r\n\r\n // 取中位数\r\n const median = getMedianResult(urlResults);\r\n allResults.push(median);\r\n } catch (error) {\r\n allResults.push({\r\n url,\r\n error: error instanceof Error ? error.message : String(error),\r\n scores: { performance: 0, accessibility: 0, bestPractices: 0, seo: 0 },\r\n metrics: {},\r\n });\r\n }\r\n }\r\n\r\n const endTime = Date.now();\r\n\r\n // 构建测试结果\r\n const suites: TestSuiteResult[] = allResults.map((result) => {\r\n const tests: TestCaseResult[] = [\r\n makeTestResult('Performance Score', result.scores.performance, options.thresholds?.performance),\r\n makeTestResult('Accessibility Score', result.scores.accessibility, options.thresholds?.accessibility),\r\n makeTestResult('Best Practices Score', result.scores.bestPractices, options.thresholds?.['best-practices']),\r\n makeTestResult('SEO Score', result.scores.seo, options.thresholds?.seo),\r\n ];\r\n\r\n // 添加核心 Web 指标测试\r\n if (result.metrics.lcp !== undefined) {\r\n tests.push(makeTestResult('Largest Contentful Paint', result.metrics.lcp, 2500, true));\r\n }\r\n if (result.metrics.fid !== undefined) {\r\n tests.push(makeTestResult('First Input Delay', result.metrics.fid, 100, true));\r\n }\r\n if (result.metrics.cls !== undefined) {\r\n tests.push(makeTestResult('Cumulative Layout Shift', result.metrics.cls, 0.1, true));\r\n }\r\n\r\n const suiteStatus: TestStatus = result.error\r\n ? 'failed'\r\n : tests.some((t) => t.status === 'failed')\r\n ? 'failed'\r\n : 'passed';\r\n\r\n return {\r\n name: `Performance: ${result.url}`,\r\n file: result.url,\r\n type: 'performance',\r\n status: suiteStatus,\r\n duration: endTime - startTime,\r\n tests,\r\n };\r\n });\r\n\r\n // 计算平均指标\r\n const avgMetrics = calculateAverageMetrics(allResults);\r\n\r\n const overallStatus = suites.some((s) => s.status === 'failed') ? 'failed' : 'passed';\r\n\r\n return {\r\n type: 'performance',\r\n status: overallStatus,\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites,\r\n performance: avgMetrics,\r\n };\r\n}\r\n\r\ninterface LighthouseRunResult {\r\n url: string;\r\n error?: string;\r\n scores: {\r\n performance: number;\r\n accessibility: number;\r\n bestPractices: number;\r\n seo: number;\r\n };\r\n metrics: {\r\n lcp?: number;\r\n fid?: number;\r\n cls?: number;\r\n };\r\n}\r\n\r\n/**\r\n * 单次 Lighthouse 运行\r\n */\r\nasync function runLighthouseOnce(url: string, extraArgs?: string[]): Promise<LighthouseRunResult> {\r\n const args = [\r\n 'lighthouse', url,\r\n '--output=json',\r\n '--quiet',\r\n '--chrome-flags=--headless --no-sandbox',\r\n '--only-categories=performance,accessibility,best-practices,seo',\r\n ];\r\n\r\n if (extraArgs) {\r\n args.push(...extraArgs);\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n execFile(npx, args, {\r\n cwd: process.cwd(),\r\n maxBuffer: 50 * 1024 * 1024,\r\n shell: true,\r\n }, (error, stdout) => {\r\n if (error && !stdout) {\r\n reject(new Error(`Lighthouse 执行失败: ${error.message}`));\r\n return;\r\n }\r\n\r\n try {\r\n const data = JSON.parse(stdout);\r\n const categories = data.categories || {};\r\n const audits = data.audits || {};\r\n\r\n resolve({\r\n url,\r\n scores: {\r\n performance: Math.round((categories.performance?.score || 0) * 100),\r\n accessibility: Math.round((categories.accessibility?.score || 0) * 100),\r\n bestPractices: Math.round((categories['best-practices']?.score || 0) * 100),\r\n seo: Math.round((categories.seo?.score || 0) * 100),\r\n },\r\n metrics: {\r\n lcp: audits['largest-contentful-paint']?.numericValue,\r\n fid: audits['max-potential-fid']?.numericValue,\r\n cls: audits['cumulative-layout-shift']?.numericValue,\r\n },\r\n });\r\n } catch {\r\n reject(new Error('Lighthouse 输出解析失败'));\r\n }\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 获取中位数结果\r\n */\r\nfunction getMedianResult(results: LighthouseRunResult[]): LighthouseRunResult {\r\n if (results.length === 1) return results[0];\r\n\r\n // 按 performance score 排序取中位数\r\n const sorted = [...results].sort(\r\n (a, b) => a.scores.performance - b.scores.performance,\r\n );\r\n const mid = Math.floor(sorted.length / 2);\r\n return sorted[mid];\r\n}\r\n\r\n/**\r\n * 创建测试结果\r\n */\r\nfunction makeTestResult(\r\n name: string,\r\n value: number,\r\n threshold?: number,\r\n isLowerBetter = false,\r\n): TestCaseResult {\r\n let status: TestStatus = 'passed';\r\n let error: { message: string } | undefined;\r\n\r\n if (threshold !== undefined) {\r\n const passed = isLowerBetter ? value <= threshold : value >= threshold;\r\n if (!passed) {\r\n status = 'failed';\r\n error = {\r\n message: `${name}: ${isLowerBetter ? `${value}ms` : value} (${isLowerBetter ? '>' : '<'} threshold ${isLowerBetter ? `${threshold}ms` : threshold})`,\r\n };\r\n }\r\n }\r\n\r\n return {\r\n name,\r\n file: 'lighthouse',\r\n status,\r\n duration: 0,\r\n error,\r\n retries: 0,\r\n };\r\n}\r\n\r\n/**\r\n * 计算平均分数\r\n */\r\nfunction calculateAverageScores(results: LighthouseRunResult[]): PerformanceMetrics {\r\n const valid = results.filter((r) => !r.error);\r\n if (valid.length === 0) {\r\n return { performance: 0, accessibility: 0, bestPractices: 0, seo: 0 };\r\n }\r\n\r\n return {\r\n performance: Math.round(valid.reduce((s, r) => s + r.scores.performance, 0) / valid.length),\r\n accessibility: Math.round(valid.reduce((s, r) => s + r.scores.accessibility, 0) / valid.length),\r\n bestPractices: Math.round(valid.reduce((s, r) => s + r.scores.bestPractices, 0) / valid.length),\r\n seo: Math.round(valid.reduce((s, r) => s + r.scores.seo, 0) / valid.length),\r\n };\r\n}\r\n\r\n/**\r\n * 计算平均指标\r\n */\r\nfunction calculateAverageMetrics(results: LighthouseRunResult[]): PerformanceMetrics {\r\n const valid = results.filter((r) => !r.error);\r\n const scores = calculateAverageScores(results);\r\n\r\n const lcpValues = valid.map((r) => r.metrics.lcp).filter((v): v is number => v !== undefined);\r\n const fidValues = valid.map((r) => r.metrics.fid).filter((v): v is number => v !== undefined);\r\n const clsValues = valid.map((r) => r.metrics.cls).filter((v): v is number => v !== undefined);\r\n\r\n return {\r\n ...scores,\r\n lcp: lcpValues.length > 0 ? lcpValues.reduce((s, v) => s + v, 0) / lcpValues.length : undefined,\r\n fid: fidValues.length > 0 ? fidValues.reduce((s, v) => s + v, 0) / fidValues.length : undefined,\r\n cls: clsValues.length > 0 ? clsValues.reduce((s, v) => s + v, 0) / clsValues.length : undefined,\r\n };\r\n}\r\n","/**\r\n * 默认空实现 - 未配置AI时的占位Provider\r\n */\r\n\r\nimport type { AIProvider, AICapability, AIGenerateTestRequest, AIGenerateTestResponse, AIAnalyzeResultRequest, AIAnalyzeResultResponse, AIReviewTestRequest, AIReviewTestResponse } from '../types/ai.js';\r\nimport type { TestError } from '../types/index.js';\r\n\r\nconst NOOP_CAPABILITIES: AICapability = {\r\n generateTest: false,\r\n analyzeResult: false,\r\n suggestFix: false,\r\n};\r\n\r\nexport class NoopAIProvider implements AIProvider {\r\n readonly name = 'noop';\r\n readonly capabilities = NOOP_CAPABILITIES;\r\n\r\n async generateTest(_req: AIGenerateTestRequest): Promise<AIGenerateTestResponse> {\r\n throw new Error(\r\n 'AI功能未配置。请在 qat.config.ts 中配置 ai.provider 后使用此功能。',\r\n );\r\n }\r\n\r\n async analyzeResult(_req: AIAnalyzeResultRequest): Promise<AIAnalyzeResultResponse> {\r\n throw new Error(\r\n 'AI功能未配置。请在 qat.config.ts 中配置 ai.provider 后使用此功能。',\r\n );\r\n }\r\n\r\n async suggestFix(_error: TestError): Promise<string[]> {\r\n throw new Error(\r\n 'AI功能未配置。请在 qat.config.ts 中配置 ai.provider 后使用此功能。',\r\n );\r\n }\r\n\r\n async reviewTest(_req: AIReviewTestRequest): Promise<AIReviewTestResponse> {\r\n throw new Error(\r\n 'AI功能未配置。请在 qat.config.ts 中配置 ai.provider 后使用此功能。',\r\n );\r\n }\r\n}\r\n","/**\r\n * OpenAI 兼容 Provider - 支持 OpenAI / DeepSeek / Moonshot / Ollama 等\r\n */\r\n\r\nimport type {\r\n AIProvider,\r\n AICapability,\r\n AIGenerateTestRequest,\r\n AIGenerateTestResponse,\r\n AIAnalyzeResultRequest,\r\n AIAnalyzeResultResponse,\r\n AIReviewTestRequest,\r\n AIReviewTestResponse,\r\n} from '../types/ai.js';\r\nimport type { TestError } from '../types/index.js';\r\n\r\n/** OpenAI Chat Completion 响应格式 */\r\ninterface ChatCompletionResponse {\r\n choices: Array<{\r\n message: {\r\n content: string;\r\n };\r\n finish_reason: string;\r\n }>;\r\n usage?: {\r\n prompt_tokens: number;\r\n completion_tokens: number;\r\n total_tokens: number;\r\n };\r\n}\r\n\r\nexport class OpenAICompatibleProvider implements AIProvider {\r\n readonly name: string;\r\n readonly capabilities: AICapability = {\r\n generateTest: true,\r\n analyzeResult: true,\r\n suggestFix: true,\r\n };\r\n\r\n private apiKey: string;\r\n private model: string;\r\n private baseUrl: string;\r\n\r\n constructor(config: { provider: string; apiKey?: string; model?: string; baseUrl?: string }) {\r\n this.name = config.provider;\r\n this.apiKey = config.apiKey || '';\r\n this.model = config.model || this.getDefaultModel(config.provider);\r\n this.baseUrl = config.baseUrl || this.getDefaultBaseUrl(config.provider);\r\n }\r\n\r\n async generateTest(req: AIGenerateTestRequest): Promise<AIGenerateTestResponse> {\r\n const systemPrompt = this.buildGenerateTestSystemPrompt(req);\r\n const userPrompt = this.buildGenerateTestUserPrompt(req);\r\n\r\n const content = await this.chat(systemPrompt, userPrompt);\r\n return this.parseGenerateTestResponse(content);\r\n }\r\n\r\n async analyzeResult(req: AIAnalyzeResultRequest): Promise<AIAnalyzeResultResponse> {\r\n const systemPrompt = `你是一个专业的测试分析专家。分析测试运行结果,找出问题根因,给出具体可操作的改进建议。\r\n输出格式:\r\n1. 分析摘要(1-3句话)\r\n2. 改进建议列表(每条建议具体、可操作)`;\r\n\r\n const resultSummary = req.testResults.map((r) => {\r\n const failed = r.suites.flatMap((s) => s.tests.filter((t) => t.status === 'failed'));\r\n return `类型: ${r.type}, 状态: ${r.status}, 耗时: ${r.duration}ms, 失败用例: ${failed.length}`;\r\n }).join('\\n');\r\n\r\n const errorDetails = req.errorLogs?.join('\\n') || req.testResults\r\n .flatMap((r) => r.suites.flatMap((s) => s.tests.filter((t) => t.status === 'failed' && t.error)))\r\n .map((t) => `[${t.name}] ${t.error?.message}`)\r\n .join('\\n') || '无错误详情';\r\n\r\n const userPrompt = `测试结果:\\n${resultSummary}\\n\\n错误详情:\\n${errorDetails}`;\r\n\r\n const content = await this.chat(systemPrompt, userPrompt);\r\n\r\n return {\r\n analysis: content.split('\\n')[0] || content.slice(0, 200),\r\n suggestions: content.split('\\n').filter((l) => l.trim().startsWith('-') || l.trim().startsWith('•') || l.trim().match(/^\\d+\\./)).map((l) => l.replace(/^[-•\\d.]+\\s*/, '').trim()).filter(Boolean),\r\n severity: req.testResults.some((r) => r.status === 'failed') ? 'error' : 'info',\r\n };\r\n }\r\n\r\n async suggestFix(error: TestError): Promise<string[]> {\r\n const systemPrompt = `你是一个专业的代码修复专家。根据测试错误信息,给出具体的修复建议。\r\n每条建议应该包含:\r\n1. 问题定位\r\n2. 修复方案\r\n3. 示例代码(如果适用)`;\r\n\r\n const userPrompt = `错误信息: ${error.message}\r\n${error.stack ? `堆栈: ${error.stack}` : ''}\r\n${error.expected ? `期望值: ${error.expected}` : ''}\r\n${error.actual ? `实际值: ${error.actual}` : ''}`;\r\n\r\n const content = await this.chat(systemPrompt, userPrompt);\r\n\r\n return content\r\n .split('\\n')\r\n .filter((l) => l.trim().startsWith('-') || l.trim().startsWith('•') || l.trim().match(/^\\d+\\./))\r\n .map((l) => l.replace(/^[-•\\d.]+\\s*/, '').trim())\r\n .filter(Boolean);\r\n }\r\n\r\n async reviewTest(req: AIReviewTestRequest): Promise<AIReviewTestResponse> {\r\n const systemPrompt = this.buildReviewTestSystemPrompt(req);\r\n const userPrompt = this.buildReviewTestUserPrompt(req);\r\n\r\n const content = await this.chat(systemPrompt, userPrompt);\r\n return this.parseReviewTestResponse(content);\r\n }\r\n\r\n // ─── 内部方法 ──────────────────────────────────────────────\r\n\r\n /**\r\n * 测试连通性 - 发送一条简单请求验证 API 可达\r\n * @returns 连通结果\r\n */\r\n async testConnection(): Promise<{ ok: boolean; message: string; latencyMs?: number }> {\r\n const url = `${this.baseUrl}/chat/completions`;\r\n const start = Date.now();\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n\r\n if (this.apiKey) {\r\n headers['Authorization'] = `Bearer ${this.apiKey}`;\r\n }\r\n\r\n try {\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify({\r\n model: this.model,\r\n messages: [{ role: 'user', content: 'Hi' }],\r\n max_tokens: 5,\r\n }),\r\n signal: AbortSignal.timeout(15000), // 15s timeout\r\n });\r\n\r\n const latencyMs = Date.now() - start;\r\n\r\n if (response.ok) {\r\n const data = (await response.json()) as ChatCompletionResponse;\r\n if (data.choices?.[0]?.message?.content !== undefined) {\r\n return { ok: true, message: `连通正常 (${latencyMs}ms)`, latencyMs };\r\n }\r\n return { ok: false, message: `API 返回格式异常: ${JSON.stringify(data).slice(0, 200)}`, latencyMs };\r\n }\r\n\r\n // 非 2xx\r\n const text = await response.text().catch(() => '');\r\n let detail = text.slice(0, 300);\r\n\r\n // 尝试提取错误信息\r\n try {\r\n const errObj = JSON.parse(text);\r\n if (errObj.error?.message) detail = errObj.error.message;\r\n } catch { /* ignore */ }\r\n\r\n if (response.status === 401) {\r\n return { ok: false, message: `认证失败: API Key 无效或已过期`, latencyMs };\r\n }\r\n if (response.status === 404) {\r\n return { ok: false, message: `接口不存在: 请检查 baseUrl 是否正确 (状态 404)`, latencyMs };\r\n }\r\n if (response.status === 429) {\r\n return { ok: false, message: `请求频率超限: 请稍后重试 (429)`, latencyMs };\r\n }\r\n\r\n return { ok: false, message: `HTTP ${response.status}: ${detail}`, latencyMs };\r\n } catch (error) {\r\n const latencyMs = Date.now() - start;\r\n if (error instanceof TypeError && error.message.includes('fetch')) {\r\n return { ok: false, message: `网络错误: 无法连接到 ${this.baseUrl},请检查地址是否正确`, latencyMs };\r\n }\r\n if (error instanceof Error && error.name === 'TimeoutError') {\r\n return { ok: false, message: `连接超时: ${this.baseUrl} 未在 15s 内响应`, latencyMs };\r\n }\r\n return { ok: false, message: `连接失败: ${error instanceof Error ? error.message : String(error)}`, latencyMs };\r\n }\r\n }\r\n\r\n private async chat(systemPrompt: string, userPrompt: string): Promise<string> {\r\n const url = `${this.baseUrl}/chat/completions`;\r\n\r\n const body = {\r\n model: this.model,\r\n messages: [\r\n { role: 'system', content: systemPrompt },\r\n { role: 'user', content: userPrompt },\r\n ],\r\n temperature: 0.3,\r\n max_tokens: 4096,\r\n };\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n\r\n // Ollama 不需要 Authorization header\r\n if (this.apiKey) {\r\n headers['Authorization'] = `Bearer ${this.apiKey}`;\r\n }\r\n\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body),\r\n signal: AbortSignal.timeout(60000), // 60s timeout\r\n });\r\n\r\n if (!response.ok) {\r\n const text = await response.text().catch(() => '');\r\n throw new Error(`AI API 请求失败 (${response.status}): ${text.slice(0, 500)}`);\r\n }\r\n\r\n const data = (await response.json()) as ChatCompletionResponse;\r\n\r\n if (!data.choices?.[0]?.message?.content) {\r\n throw new Error('AI API 返回空响应');\r\n }\r\n\r\n return data.choices[0].message.content;\r\n }\r\n\r\n private buildGenerateTestSystemPrompt(req: AIGenerateTestRequest): string {\r\n const typeMap: Record<string, string> = {\r\n unit: '单元测试(Vitest + @vue/test-utils)',\r\n component: '组件测试(Vitest + @vue/test-utils + mount)',\r\n e2e: 'E2E端到端测试(Playwright)',\r\n api: 'API接口测试(Vitest + fetch)',\r\n visual: '视觉回归测试(Playwright screenshot)',\r\n performance: '性能测试(Playwright + performance metrics)',\r\n };\r\n\r\n return `你是一个专业的前端测试工程师,擅长编写高质量的${typeMap[req.type] || req.type}。\r\n要求:\r\n1. 只输出测试代码,不要多余的解释\r\n2. 代码必须可直接运行,包含所有必要的 import\r\n3. 测试用例覆盖:正常路径、边界条件、错误处理\r\n4. 使用中文描述 it/test 块名称\r\n5. Vue 组件测试使用 @vue/test-utils 的 mount\r\n6. 如果有 props/emits 信息,务必针对每个 prop 和 emit 生成测试`;\r\n }\r\n\r\n private buildGenerateTestUserPrompt(req: AIGenerateTestRequest): string {\r\n let prompt = `请为以下文件生成${req.type}测试代码:\\n目标文件: ${req.target}\\n`;\r\n\r\n if (req.analysis) {\r\n prompt += '\\n源码分析结果:\\n';\r\n\r\n if (req.analysis.exports?.length > 0) {\r\n prompt += `导出项:\\n${req.analysis.exports.map((e) => {\r\n const params = e.params?.length ? `(${e.params.join(', ')})` : '';\r\n const asyncFlag = e.isAsync ? 'async ' : '';\r\n return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;\r\n }).join('\\n')}\\n`;\r\n }\r\n\r\n if (req.analysis.props?.length) {\r\n prompt += `Props:\\n${req.analysis.props.map((p) =>\r\n ` - ${p.name}: ${p.type}${p.required ? ' (必填)' : ' (可选)'}`,\r\n ).join('\\n')}\\n`;\r\n }\r\n\r\n if (req.analysis.emits?.length) {\r\n prompt += `Emits:\\n${req.analysis.emits.map((e) =>\r\n ` - ${e.name}${e.params?.length ? `(${e.params.join(', ')})` : ''}`,\r\n ).join('\\n')}\\n`;\r\n }\r\n\r\n if (req.analysis.methods?.length) {\r\n prompt += `Methods: ${req.analysis.methods.join(', ')}\\n`;\r\n }\r\n\r\n if (req.analysis.computed?.length) {\r\n prompt += `Computed: ${req.analysis.computed.join(', ')}\\n`;\r\n }\r\n }\r\n\r\n if (req.context) {\r\n prompt += `\\n源码内容:\\n\\`\\`\\`typescript\\n${req.context}\\n\\`\\`\\`\\n`;\r\n }\r\n\r\n if (req.framework) {\r\n prompt += `\\n框架: ${req.framework}`;\r\n }\r\n\r\n return prompt;\r\n }\r\n\r\n private parseGenerateTestResponse(content: string): AIGenerateTestResponse {\r\n // 提取代码块\r\n const codeBlockMatch = content.match(/```(?:typescript|ts|javascript|js)?\\s*\\n([\\s\\S]*?)```/);\r\n const code = codeBlockMatch\r\n ? codeBlockMatch[1].trim()\r\n : content.replace(/^(?:```[\\s\\S]*?\\n)?/, '').replace(/\\n?```$/, '').trim();\r\n\r\n // 提取描述(代码块之前的文字)\r\n const description = codeBlockMatch\r\n ? content.split('```')[0].trim().slice(0, 200)\r\n : 'AI generated test';\r\n\r\n return {\r\n code,\r\n description: description || 'AI generated test',\r\n confidence: 0.8,\r\n };\r\n }\r\n\r\n private buildReviewTestSystemPrompt(_req: AIReviewTestRequest): string {\r\n return `你是一位严谨的测试审计专家。你的职责是审查 AI 生成的测试用例是否与源码贴切且准确。\r\n\r\n审查标准:\r\n1. **测试类型匹配**:测试类型是否与源码文件性质匹配(如 .vue 应为组件测试,utils 应为单元测试)\r\n2. **覆盖完整性**:是否覆盖了源码中的核心导出项(函数、组件的 props/emits/methods)\r\n3. **断言有效性**:断言是否真实检验了被测行为,而非空断言或永真断言\r\n4. **导入正确性**:import 路径和模块是否正确\r\n5. **代码可运行性**:测试代码是否可直接运行,无语法错误\r\n\r\n输出格式(严格遵守):\r\nAPPROVED: true 或 false\r\nSCORE: 0.0 到 1.0 之间的评分\r\nFEEDBACK: 一句话审计意见\r\nISSUES: 问题列表(每行一个,格式 \"- 问题描述\")\r\nSUGGESTIONS: 改进建议列表(每行一个,格式 \"- 建议描述\")`;\r\n }\r\n\r\n private buildReviewTestUserPrompt(req: AIReviewTestRequest): string {\r\n let prompt = `请审查以下测试用例是否与源码贴切且准确。\r\n\r\n被测文件: ${req.target}\r\n测试类型: ${req.testType}\r\n\r\n--- 源码内容 ---\r\n\\`\\`\\`typescript\r\n${req.sourceCode}\r\n\\`\\`\\`\r\n\r\n--- 生成的测试代码 ---\r\n\\`\\`\\`typescript\r\n${req.testCode}\r\n\\`\\`\\``;\r\n\r\n if (req.analysis) {\r\n prompt += '\\n\\n--- 源码分析 ---';\r\n\r\n if (req.analysis.exports?.length > 0) {\r\n prompt += `\\n导出项:\\n${req.analysis.exports.map((e) => {\r\n const params = e.params?.length ? `(${e.params.join(', ')})` : '';\r\n const asyncFlag = e.isAsync ? 'async ' : '';\r\n return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;\r\n }).join('\\n')}`;\r\n }\r\n\r\n if (req.analysis.props?.length) {\r\n prompt += `\\nProps:\\n${req.analysis.props.map((p) =>\r\n ` - ${p.name}: ${p.type}${p.required ? ' (必填)' : ' (可选)'}`,\r\n ).join('\\n')}`;\r\n }\r\n\r\n if (req.analysis.emits?.length) {\r\n prompt += `\\nEmits:\\n${req.analysis.emits.map((e) =>\r\n ` - ${e.name}${e.params?.length ? `(${e.params.join(', ')})` : ''}`,\r\n ).join('\\n')}`;\r\n }\r\n }\r\n\r\n if (req.generationDescription) {\r\n prompt += `\\n\\n生成者说明: ${req.generationDescription}`;\r\n }\r\n\r\n return prompt;\r\n }\r\n\r\n private parseReviewTestResponse(content: string): AIReviewTestResponse {\r\n const approvedMatch = content.match(/APPROVED:\\s*(true|false)/i);\r\n const scoreMatch = content.match(/SCORE:\\s*([\\d.]+)/);\r\n const feedbackMatch = content.match(/FEEDBACK:\\s*(.+)/);\r\n\r\n const approved = approvedMatch ? approvedMatch[1].toLowerCase() === 'true' : false;\r\n const score = scoreMatch ? parseFloat(scoreMatch[1]) : (approved ? 0.7 : 0.3);\r\n const feedback = feedbackMatch ? feedbackMatch[1].trim() : '审计完成';\r\n\r\n // 提取问题列表\r\n const issues: string[] = [];\r\n const issuesMatch = content.match(/ISSUES:\\s*\\n([\\s\\S]*?)(?=SUGGESTIONS:|$)/);\r\n if (issuesMatch) {\r\n const lines = issuesMatch[1].split('\\n');\r\n for (const line of lines) {\r\n const trimmed = line.replace(/^[-•\\d.]+\\s*/, '').trim();\r\n if (trimmed) issues.push(trimmed);\r\n }\r\n }\r\n\r\n // 提取建议列表\r\n const suggestions: string[] = [];\r\n const suggestionsMatch = content.match(/SUGGESTIONS:\\s*\\n([\\s\\S]*?)$/);\r\n if (suggestionsMatch) {\r\n const lines = suggestionsMatch[1].split('\\n');\r\n for (const line of lines) {\r\n const trimmed = line.replace(/^[-•\\d.]+\\s*/, '').trim();\r\n if (trimmed) suggestions.push(trimmed);\r\n }\r\n }\r\n\r\n return {\r\n approved,\r\n score: Math.max(0, Math.min(1, score)),\r\n feedback,\r\n issues,\r\n suggestions,\r\n };\r\n }\r\n\r\n private getDefaultModel(provider: string): string {\r\n const modelMap: Record<string, string> = {\r\n openai: 'gpt-4o-mini',\r\n deepseek: 'deepseek-chat',\r\n moonshot: 'moonshot-v1-8k',\r\n zhipu: 'glm-4-flash',\r\n qwen: 'qwen-turbo',\r\n ollama: 'qwen2.5-coder:7b',\r\n };\r\n return modelMap[provider] || 'gpt-4o-mini';\r\n }\r\n\r\n private getDefaultBaseUrl(provider: string): string {\r\n const urlMap: Record<string, string> = {\r\n openai: 'https://api.openai.com/v1',\r\n deepseek: 'https://api.deepseek.com/v1',\r\n moonshot: 'https://api.moonshot.cn/v1',\r\n zhipu: 'https://open.bigmodel.cn/api/paas/v4',\r\n qwen: 'https://dashscope.aliyuncs.com/compatible-mode/v1',\r\n ollama: 'http://localhost:11434/v1',\r\n };\r\n return urlMap[provider] || 'https://api.openai.com/v1';\r\n }\r\n}\r\n","/**\r\n * AI Provider 管理中心 - 注册、加载、调度 AI Provider\r\n */\r\n\r\nimport type { AIConfig, AIPresetProvider } from '../types/ai.js';\r\nimport { NoopAIProvider } from './noop-provider.js';\r\nimport { OpenAICompatibleProvider } from './openai-provider.js';\r\n\r\n/** Provider 构造函数类型 */\r\nexport type AIProviderConstructor = new (config: AIConfig) => NoopAIProvider | OpenAICompatibleProvider;\r\n\r\n/** Provider 注册表 */\r\nconst providerRegistry = new Map<string, AIProviderConstructor>();\r\n\r\n/** 当前活跃的 Provider 实例 */\r\nlet activeProvider: NoopAIProvider | OpenAICompatibleProvider | null = null;\r\n\r\n// ─── 自动注册内置 Provider ──────────────────────────────────────\r\n\r\nconst BUILTIN_PROVIDERS: Array<{ id: string; ProviderClass: AIProviderConstructor }> = [\r\n { id: 'openai', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'deepseek', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'moonshot', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'zhipu', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'qwen', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'ollama', ProviderClass: OpenAICompatibleProvider },\r\n];\r\n\r\nfor (const { id, ProviderClass } of BUILTIN_PROVIDERS) {\r\n providerRegistry.set(id, ProviderClass);\r\n}\r\n\r\n/** 预置 Provider 信息(用于 init 向导展示) */\r\nexport const AI_PRESET_PROVIDERS: AIPresetProvider[] = [\r\n { id: 'openai', name: 'OpenAI (GPT-4o)', baseUrl: 'https://api.openai.com/v1', defaultModel: 'gpt-4o-mini', requiresApiKey: true },\r\n { id: 'deepseek', name: 'DeepSeek', baseUrl: 'https://api.deepseek.com/v1', defaultModel: 'deepseek-chat', requiresApiKey: true },\r\n { id: 'moonshot', name: 'Moonshot (月之暗面)', baseUrl: 'https://api.moonshot.cn/v1', defaultModel: 'moonshot-v1-8k', requiresApiKey: true },\r\n { id: 'zhipu', name: '智谱 (GLM-4)', baseUrl: 'https://open.bigmodel.cn/api/paas/v4', defaultModel: 'glm-4-flash', requiresApiKey: true },\r\n { id: 'qwen', name: '通义千问 (Qwen)', baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', defaultModel: 'qwen-turbo', requiresApiKey: true },\r\n { id: 'ollama', name: 'Ollama (本地)', baseUrl: 'http://localhost:11434/v1', defaultModel: 'qwen2.5-coder:7b', requiresApiKey: false },\r\n];\r\n\r\n/**\r\n * 注册 AI Provider\r\n * @param name Provider 名称\r\n * @param ProviderClass Provider 类\r\n */\r\nexport function registerAIProvider(name: string, ProviderClass: AIProviderConstructor): void {\r\n providerRegistry.set(name, ProviderClass);\r\n}\r\n\r\n/**\r\n * 获取已注册的 Provider 名称列表\r\n */\r\nexport function getRegisteredProviders(): string[] {\r\n return Array.from(providerRegistry.keys());\r\n}\r\n\r\n/**\r\n * 获取 AI Provider 实例\r\n * @param config AI 配置,若未配置则返回 NoopProvider\r\n */\r\nexport function getAIProvider(config?: AIConfig): NoopAIProvider | OpenAICompatibleProvider {\r\n if (activeProvider) {\r\n return activeProvider;\r\n }\r\n\r\n if (!config || !config.provider) {\r\n activeProvider = new NoopAIProvider();\r\n return activeProvider;\r\n }\r\n\r\n const ProviderClass = providerRegistry.get(config.provider);\r\n if (!ProviderClass) {\r\n console.warn(\r\n `未找到 AI Provider \"${config.provider}\",已注册的 Provider: ${\r\n providerRegistry.size > 0 ? getRegisteredProviders().join(', ') : '无'\r\n }。将使用默认 Provider。`,\r\n );\r\n activeProvider = new NoopAIProvider();\r\n return activeProvider;\r\n }\r\n\r\n activeProvider = new ProviderClass(config);\r\n return activeProvider;\r\n}\r\n\r\n/**\r\n * 重置 Provider 实例(用于配置变更后重新初始化)\r\n */\r\nexport function resetAIProvider(): void {\r\n activeProvider = null;\r\n}\r\n\r\n/**\r\n * 检查 AI 功能是否可用\r\n */\r\nexport function isAIAvailable(config?: AIConfig): boolean {\r\n if (!config || !config.provider) return false;\r\n return providerRegistry.has(config.provider);\r\n}\r\n\r\n/**\r\n * 创建独立的 AI Provider 实例(不使用单例缓存)\r\n * 用于需要多个独立上下文的场景,如生成者+审计员双模型\r\n */\r\nexport function createAIProvider(config: AIConfig): NoopAIProvider | OpenAICompatibleProvider {\r\n if (!config || !config.provider) {\r\n return new NoopAIProvider();\r\n }\r\n\r\n const ProviderClass = providerRegistry.get(config.provider);\r\n if (!ProviderClass) {\r\n return new NoopAIProvider();\r\n }\r\n\r\n return new ProviderClass(config);\r\n}\r\n\r\n/**\r\n * 测试 AI 连通性\r\n */\r\nexport async function testAIConnection(config: AIConfig): Promise<{ ok: boolean; message: string; latencyMs?: number }> {\r\n const ProviderClass = providerRegistry.get(config.provider);\r\n if (!ProviderClass) {\r\n return { ok: false, message: `未注册的 Provider: ${config.provider},可选: ${getRegisteredProviders().join(', ')}` };\r\n }\r\n\r\n const provider = new ProviderClass(config);\r\n\r\n if (!(provider instanceof OpenAICompatibleProvider)) {\r\n return { ok: false, message: `${config.provider} 不支持连通性测试` };\r\n }\r\n\r\n return provider.testConnection();\r\n}\r\n\r\n// 重新导出类型\r\nexport type {\r\n AICapability,\r\n AIGenerateTestRequest,\r\n AIGenerateTestResponse,\r\n AIAnalyzeResultRequest,\r\n AIAnalyzeResultResponse,\r\n AIReviewTestRequest,\r\n AIReviewTestResponse,\r\n AIProvider,\r\n AIConfig,\r\n} from '../types/ai.js';\r\n","/**\r\n * Mock服务 - Express服务器,路由管理,数据模板\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport type { Request, Response, NextFunction } from 'express';\r\n\r\n/** Mock路由配置 */\r\nexport interface MockRoute {\r\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\r\n path: string;\r\n status?: number;\r\n response?: unknown;\r\n delay?: number;\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/** Mock服务器状态 */\r\nexport interface MockServerState {\r\n running: boolean;\r\n port: number;\r\n routes: MockRoute[];\r\n pid?: number;\r\n}\r\n\r\n/** 全局Mock服务器状态 */\r\nlet serverState: MockServerState = {\r\n running: false,\r\n port: 3456,\r\n routes: [],\r\n};\r\n\r\n/** 服务器实例引用 */\r\nlet serverInstance: ReturnType<typeof import('http').createServer> | null = null;\r\n\r\n/**\r\n * 获取Mock服务器状态\r\n */\r\nexport function getMockServerState(): MockServerState {\r\n return { ...serverState };\r\n}\r\n\r\n/**\r\n * 加载Mock路由配置\r\n * 支持从目录加载 .json 和 .js/.ts 路由文件\r\n */\r\nexport async function loadMockRoutes(routesDir: string): Promise<MockRoute[]> {\r\n const absDir = path.resolve(process.cwd(), routesDir);\r\n\r\n if (!fs.existsSync(absDir)) {\r\n return [];\r\n }\r\n\r\n const routes: MockRoute[] = [];\r\n const entries = fs.readdirSync(absDir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n if (!entry.isFile()) continue;\r\n\r\n const filePath = path.join(absDir, entry.name);\r\n const ext = path.extname(entry.name);\r\n\r\n try {\r\n if (ext === '.json') {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n const parsed = JSON.parse(content);\r\n if (Array.isArray(parsed)) {\r\n routes.push(...parsed);\r\n } else if (parsed.method && parsed.path) {\r\n routes.push(parsed as MockRoute);\r\n }\r\n } else if (ext === '.js' || ext === '.mjs') {\r\n const module = await import(filePath);\r\n const exported = module.default || module;\r\n if (Array.isArray(exported)) {\r\n routes.push(...exported);\r\n } else if (exported.method && exported.path) {\r\n routes.push(exported as MockRoute);\r\n }\r\n }\r\n } catch (error) {\r\n console.warn(`加载路由文件失败 ${entry.name}: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n }\r\n\r\n return routes;\r\n}\r\n\r\n/**\r\n * 创建默认Mock路由\r\n */\r\nexport function createDefaultRoutes(): MockRoute[] {\r\n return [\r\n {\r\n method: 'GET',\r\n path: '/api/health',\r\n status: 200,\r\n response: { status: 'ok', timestamp: Date.now() },\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/*',\r\n status: 200,\r\n response: { message: 'Mock API response', data: null },\r\n delay: 100,\r\n },\r\n {\r\n method: 'POST',\r\n path: '/api/*',\r\n status: 201,\r\n response: { message: 'Created successfully', data: null },\r\n },\r\n {\r\n method: 'PUT',\r\n path: '/api/*',\r\n status: 200,\r\n response: { message: 'Updated successfully', data: null },\r\n },\r\n {\r\n method: 'DELETE',\r\n path: '/api/*',\r\n status: 204,\r\n response: null,\r\n },\r\n ];\r\n}\r\n\r\n/**\r\n * 启动Mock服务器\r\n */\r\nexport async function startMockServer(port: number, routes: MockRoute[]): Promise<void> {\r\n if (serverState.running) {\r\n throw new Error(`Mock服务器已在运行 (端口: ${serverState.port})`);\r\n }\r\n\r\n const express = await import('express');\r\n const app = express.default();\r\n\r\n // 中间件\r\n app.use(express.default.json());\r\n\r\n // 请求日志\r\n app.use((req: Request, _res: Response, next: NextFunction) => {\r\n if (process.env.QAT_VERBOSE === 'true') {\r\n console.log(` [Mock] ${req.method} ${req.url}`);\r\n }\r\n next();\r\n });\r\n\r\n // 注册路由\r\n const allRoutes = routes.length > 0 ? routes : createDefaultRoutes();\r\n\r\n for (const route of allRoutes) {\r\n const handler = async (req: Request, res: Response) => {\r\n // 模拟延迟\r\n if (route.delay && route.delay > 0) {\r\n await new Promise((resolve) => setTimeout(resolve, route.delay));\r\n }\r\n\r\n // 设置自定义响应头\r\n if (route.headers) {\r\n for (const [key, value] of Object.entries(route.headers)) {\r\n res.setHeader(key, value);\r\n }\r\n }\r\n\r\n res.setHeader('X-Mock-Server', 'qat');\r\n\r\n // 替换响应中的请求参数\r\n let response = route.response;\r\n if (typeof response === 'object' && response !== null) {\r\n const responseStr = JSON.stringify(response)\r\n .replace(/\\{\\{params\\.(\\w+)\\}\\}/g, (_match, key: string) => (req.params[key] as string) || '')\r\n .replace(/\\{\\{query\\.(\\w+)\\}\\}/g, (_match, key: string) => (req.query[key] as string) || '')\r\n .replace(/\\{\\{body\\.(\\w+)\\}\\}/g, (_match, key: string) => String((req.body as Record<string, unknown>)?.[key] ?? ''));\r\n try {\r\n response = JSON.parse(responseStr);\r\n } catch {\r\n // 保持原始响应\r\n }\r\n }\r\n\r\n res.status(route.status || 200).json(response);\r\n };\r\n\r\n // 根据方法注册路由\r\n const expressRoute = route.path;\r\n switch (route.method) {\r\n case 'GET':\r\n app.get(expressRoute, handler);\r\n break;\r\n case 'POST':\r\n app.post(expressRoute, handler);\r\n break;\r\n case 'PUT':\r\n app.put(expressRoute, handler);\r\n break;\r\n case 'DELETE':\r\n app.delete(expressRoute, handler);\r\n break;\r\n case 'PATCH':\r\n app.patch(expressRoute, handler);\r\n break;\r\n }\r\n }\r\n\r\n // 404 兜底\r\n app.use((req, res) => {\r\n res.status(404).json({\r\n error: 'Not Found',\r\n message: `No mock route defined for ${req.method} ${req.url}`,\r\n hint: '在 tests/mock/routes/ 目录下添加路由配置',\r\n });\r\n });\r\n\r\n // 启动服务器\r\n return new Promise((resolve, reject) => {\r\n serverInstance = app.listen(port, () => {\r\n serverState = {\r\n running: true,\r\n port,\r\n routes: allRoutes,\r\n pid: process.pid,\r\n };\r\n resolve();\r\n });\r\n\r\n serverInstance.on('error', (err: Error) => {\r\n reject(new Error(`Mock服务器启动失败: ${err.message}`));\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 停止Mock服务器\r\n */\r\nexport async function stopMockServer(): Promise<void> {\r\n if (!serverInstance || !serverState.running) {\r\n serverState = { ...serverState, running: false };\r\n return;\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n serverInstance!.close((err) => {\r\n if (err) {\r\n reject(new Error(`Mock服务器停止失败: ${err.message}`));\r\n return;\r\n }\r\n\r\n serverInstance = null;\r\n serverState = {\r\n running: false,\r\n port: serverState.port,\r\n routes: [],\r\n };\r\n resolve();\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 生成Mock路由配置文件模板\r\n */\r\nexport function generateMockRouteTemplate(name: string): string {\r\n return `[\r\n {\r\n method: 'GET',\r\n path: '/api/${name}',\r\n status: 200,\r\n response: {\r\n message: 'Success',\r\n data: {\r\n id: 1,\r\n name: '${name}',\r\n },\r\n },\r\n delay: 100,\r\n },\r\n {\r\n method: 'POST',\r\n path: '/api/${name}',\r\n status: 201,\r\n response: {\r\n message: 'Created',\r\n data: {\r\n id: 2,\r\n name: '{{body.name}}',\r\n },\r\n },\r\n },\r\n {\r\n method: 'DELETE',\r\n path: '/api/${name}/:id',\r\n status: 204,\r\n response: null,\r\n },\r\n]\r\n`;\r\n}\r\n\r\n/**\r\n * 初始化Mock路由目录\r\n */\r\nexport function initMockRoutesDir(routesDir: string): void {\r\n const absDir = path.resolve(process.cwd(), routesDir);\r\n\r\n if (!fs.existsSync(absDir)) {\r\n fs.mkdirSync(absDir, { recursive: true });\r\n }\r\n\r\n // 生成示例路由文件\r\n const examplePath = path.join(absDir, 'example.json');\r\n if (!fs.existsSync(examplePath)) {\r\n const exampleRoutes: MockRoute[] = [\r\n {\r\n method: 'GET',\r\n path: '/api/users',\r\n status: 200,\r\n response: {\r\n message: 'Success',\r\n data: [\r\n { id: 1, name: 'Alice' },\r\n { id: 2, name: 'Bob' },\r\n ],\r\n },\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/users/:id',\r\n status: 200,\r\n response: {\r\n message: 'Success',\r\n data: { id: 1, name: 'Alice' },\r\n },\r\n },\r\n ];\r\n fs.writeFileSync(examplePath, JSON.stringify(exampleRoutes, null, 2), 'utf-8');\r\n }\r\n}\r\n","/**\r\n * 视觉回归服务 - 截图比对、基线管理、差异图片生成\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport pixelmatch from 'pixelmatch';\r\nimport { PNG } from 'pngjs';\r\n\r\n/** 比对结果 */\r\nexport interface DiffResult {\r\n /** 是否通过(差异在阈值内) */\r\n passed: boolean;\r\n /** 差异像素数 */\r\n diffPixels: number;\r\n /** 总像素数 */\r\n totalPixels: number;\r\n /** 差异比例 (0-1) */\r\n diffRatio: number;\r\n /** 基线图片路径 */\r\n baselinePath: string;\r\n /** 当前图片路径 */\r\n currentPath: string;\r\n /** 差异图片路径(仅在有差异时生成) */\r\n diffPath?: string;\r\n}\r\n\r\n/**\r\n * 比对两张图片\r\n */\r\nexport function compareImages(\r\n baselinePath: string,\r\n currentPath: string,\r\n diffOutputPath: string,\r\n threshold: number = 0.1,\r\n): DiffResult {\r\n // 读取基线图片\r\n if (!fs.existsSync(baselinePath)) {\r\n throw new Error(`基线图片不存在: ${baselinePath}`);\r\n }\r\n\r\n if (!fs.existsSync(currentPath)) {\r\n throw new Error(`当前图片不存在: ${currentPath}`);\r\n }\r\n\r\n const baseline = PNG.sync.read(fs.readFileSync(baselinePath));\r\n const current = PNG.sync.read(fs.readFileSync(currentPath));\r\n\r\n // 尺寸必须一致\r\n if (baseline.width !== current.width || baseline.height !== current.height) {\r\n return {\r\n passed: false,\r\n diffPixels: -1,\r\n totalPixels: baseline.width * baseline.height,\r\n diffRatio: 1,\r\n baselinePath,\r\n currentPath,\r\n diffPath: undefined,\r\n };\r\n }\r\n\r\n const { width, height } = baseline;\r\n const totalPixels = width * height;\r\n\r\n // 创建差异图片\r\n const diff = new PNG({ width, height });\r\n const diffPixels = pixelmatch(\r\n baseline.data,\r\n current.data,\r\n diff.data,\r\n width,\r\n height,\r\n { threshold: 0.1 }, // pixelmatch 自身阈值(颜色差异灵敏度)\r\n );\r\n\r\n const diffRatio = diffPixels / totalPixels;\r\n const passed = diffRatio <= threshold;\r\n\r\n // 生成差异图片(仅在有差异时)\r\n let diffPath: string | undefined;\r\n if (diffPixels > 0) {\r\n // 确保输出目录存在\r\n const diffDir = path.dirname(diffOutputPath);\r\n if (!fs.existsSync(diffDir)) {\r\n fs.mkdirSync(diffDir, { recursive: true });\r\n }\r\n fs.writeFileSync(diffOutputPath, PNG.sync.write(diff));\r\n diffPath = diffOutputPath;\r\n }\r\n\r\n return {\r\n passed,\r\n diffPixels,\r\n totalPixels,\r\n diffRatio,\r\n baselinePath,\r\n currentPath,\r\n diffPath,\r\n };\r\n}\r\n\r\n/**\r\n * 创建基线快照(如果不存在则从当前截图复制)\r\n */\r\nexport function createBaseline(\r\n currentPath: string,\r\n baselinePath: string,\r\n): string {\r\n if (!fs.existsSync(currentPath)) {\r\n throw new Error(`当前截图不存在: ${currentPath}`);\r\n }\r\n\r\n const baselineDir = path.dirname(baselinePath);\r\n if (!fs.existsSync(baselineDir)) {\r\n fs.mkdirSync(baselineDir, { recursive: true });\r\n }\r\n\r\n fs.copyFileSync(currentPath, baselinePath);\r\n return baselinePath;\r\n}\r\n\r\n/**\r\n * 更新基线(将当前截图替换为基线)\r\n */\r\nexport function updateBaseline(\r\n currentPath: string,\r\n baselinePath: string,\r\n): string {\r\n return createBaseline(currentPath, baselinePath);\r\n}\r\n\r\n/**\r\n * 批量更新基线(将 diff 目录下的所有当前截图更新为基线)\r\n */\r\nexport function updateAllBaselines(\r\n currentDir: string,\r\n baselineDir: string,\r\n): string[] {\r\n const updated: string[] = [];\r\n\r\n if (!fs.existsSync(currentDir)) {\r\n return updated;\r\n }\r\n\r\n const files = fs.readdirSync(currentDir).filter((f) => f.endsWith('.png'));\r\n\r\n for (const file of files) {\r\n const currentPath = path.join(currentDir, file);\r\n const baselinePath = path.join(baselineDir, file);\r\n\r\n const baselineDirAbs = path.dirname(baselinePath);\r\n if (!fs.existsSync(baselineDirAbs)) {\r\n fs.mkdirSync(baselineDirAbs, { recursive: true });\r\n }\r\n\r\n fs.copyFileSync(currentPath, baselinePath);\r\n updated.push(file);\r\n }\r\n\r\n return updated;\r\n}\r\n\r\n/**\r\n * 清理所有基线快照\r\n */\r\nexport function cleanBaselines(baselineDir: string): number {\r\n if (!fs.existsSync(baselineDir)) {\r\n return 0;\r\n }\r\n\r\n const files = fs.readdirSync(baselineDir).filter((f) => f.endsWith('.png'));\r\n let count = 0;\r\n\r\n for (const file of files) {\r\n fs.unlinkSync(path.join(baselineDir, file));\r\n count++;\r\n }\r\n\r\n return count;\r\n}\r\n\r\n/**\r\n * 清理差异图片\r\n */\r\nexport function cleanDiffs(diffDir: string): number {\r\n if (!fs.existsSync(diffDir)) {\r\n return 0;\r\n }\r\n\r\n const files = fs.readdirSync(diffDir).filter((f) => f.endsWith('.png'));\r\n let count = 0;\r\n\r\n for (const file of files) {\r\n fs.unlinkSync(path.join(diffDir, file));\r\n count++;\r\n }\r\n\r\n return count;\r\n}\r\n\r\n/**\r\n * 获取基线快照列表\r\n */\r\nexport function listBaselines(baselineDir: string): string[] {\r\n if (!fs.existsSync(baselineDir)) {\r\n return [];\r\n }\r\n\r\n return fs\r\n .readdirSync(baselineDir)\r\n .filter((f) => f.endsWith('.png'))\r\n .sort();\r\n}\r\n\r\n/**\r\n * 获取差异图片列表\r\n */\r\nexport function listDiffs(diffDir: string): string[] {\r\n if (!fs.existsSync(diffDir)) {\r\n return [];\r\n }\r\n\r\n return fs\r\n .readdirSync(diffDir)\r\n .filter((f) => f.endsWith('.png'))\r\n .sort();\r\n}\r\n\r\n/**\r\n * 批量比对目录中的所有截图\r\n */\r\nexport function compareDirectories(\r\n baselineDir: string,\r\n currentDir: string,\r\n diffDir: string,\r\n threshold: number = 0.1,\r\n): DiffResult[] {\r\n const results: DiffResult[] = [];\r\n\r\n if (!fs.existsSync(currentDir)) {\r\n return results;\r\n }\r\n\r\n const currentFiles = fs.readdirSync(currentDir).filter((f) => f.endsWith('.png'));\r\n\r\n for (const file of currentFiles) {\r\n const currentPath = path.join(currentDir, file);\r\n const baselinePath = path.join(baselineDir, file);\r\n const diffPath = path.join(diffDir, file);\r\n\r\n if (!fs.existsSync(baselinePath)) {\r\n // 无基线,自动创建\r\n createBaseline(currentPath, baselinePath);\r\n results.push({\r\n passed: true,\r\n diffPixels: 0,\r\n totalPixels: 0,\r\n diffRatio: 0,\r\n baselinePath,\r\n currentPath,\r\n diffPath: undefined,\r\n });\r\n continue;\r\n }\r\n\r\n try {\r\n const result = compareImages(baselinePath, currentPath, diffPath, threshold);\r\n results.push(result);\r\n } catch (error) {\r\n results.push({\r\n passed: false,\r\n diffPixels: -1,\r\n totalPixels: 0,\r\n diffRatio: 1,\r\n baselinePath,\r\n currentPath,\r\n diffPath: undefined,\r\n });\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n","/**\r\n * 源码分析器 - 扫描 TS/JS/Vue 文件,提取导出、Props、Events、API调用 等信息\r\n * 用于生成个性化的测试用例和 Mock 路由\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\n\r\n// ─── 分析结果类型 ────────────────────────────────────────────\r\n\r\n/** 导出项类型 */\r\nexport type ExportKind = 'function' | 'class' | 'const' | 'type' | 'default' | 'component';\r\n\r\n/** 导出项信息 */\r\nexport interface ExportInfo {\r\n /** 导出名称 */\r\n name: string;\r\n /** 导出类型 */\r\n kind: ExportKind;\r\n /** 函数参数列表 */\r\n params: string[];\r\n /** 函数是否有返回值类型注解 */\r\n returnType?: string;\r\n /** 是否为异步函数 */\r\n isAsync: boolean;\r\n}\r\n\r\n/** Vue 组件 Props 信息 */\r\nexport interface PropInfo {\r\n /** prop 名称 */\r\n name: string;\r\n /** prop 类型(如 String, Number, Boolean, Array, Object) */\r\n type: string;\r\n /** 是否必填 */\r\n required: boolean;\r\n /** 默认值 */\r\n defaultValue?: string;\r\n}\r\n\r\n/** Vue 组件 Emits 信息 */\r\nexport interface EmitInfo {\r\n /** 事件名称 */\r\n name: string;\r\n /** 事件参数 */\r\n params: string[];\r\n}\r\n\r\n/** Vue 组件分析结果 */\r\nexport interface VueComponentAnalysis {\r\n /** 组件名称 */\r\n componentName: string;\r\n /** Props 列表 */\r\n props: PropInfo[];\r\n /** Emits 列表 */\r\n emits: EmitInfo[];\r\n /** 组件中定义的方法名 */\r\n methods: string[];\r\n /** 组件中定义的 computed 名 */\r\n computed: string[];\r\n /** 是否使用 setup 语法 */\r\n usesSetup: boolean;\r\n}\r\n\r\n/** API 调用信息 */\r\nexport interface APICallInfo {\r\n /** HTTP 方法 */\r\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\r\n /** 请求路径 */\r\n url: string;\r\n /** 来源文件 */\r\n sourceFile: string;\r\n}\r\n\r\n/** TS/JS 文件分析结果 */\r\nexport interface ModuleAnalysis {\r\n /** 文件路径 */\r\n filePath: string;\r\n /** 导出项列表 */\r\n exports: ExportInfo[];\r\n /** Vue 组件分析(仅 .vue 文件) */\r\n vueAnalysis?: VueComponentAnalysis;\r\n /** 发现的 API 调用 */\r\n apiCalls: APICallInfo[];\r\n}\r\n\r\n// ─── 主分析函数 ──────────────────────────────────────────────\r\n\r\n/**\r\n * 分析一个源码文件\r\n */\r\nexport function analyzeFile(filePath: string): ModuleAnalysis {\r\n const absolutePath = path.resolve(process.cwd(), filePath);\r\n\r\n if (!fs.existsSync(absolutePath)) {\r\n return { filePath, exports: [], apiCalls: [] };\r\n }\r\n\r\n const content = fs.readFileSync(absolutePath, 'utf-8');\r\n const ext = path.extname(filePath);\r\n\r\n const result: ModuleAnalysis = {\r\n filePath,\r\n exports: extractExports(content, ext),\r\n apiCalls: extractAPICalls(content, filePath),\r\n };\r\n\r\n // Vue 组件分析\r\n if (ext === '.vue') {\r\n result.vueAnalysis = analyzeVueComponent(content, path.basename(filePath, '.vue'));\r\n // Vue 文件的 <script> 部分也提取 API 调用\r\n const scriptMatch = content.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\r\n if (scriptMatch) {\r\n result.apiCalls = extractAPICalls(scriptMatch[1], filePath);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 批量分析文件\r\n */\r\nexport function analyzeFiles(filePaths: string[]): ModuleAnalysis[] {\r\n return filePaths.map(analyzeFile);\r\n}\r\n\r\n// ─── 导出提取 ────────────────────────────────────────────────\r\n\r\n/**\r\n * 从源码中提取导出项\r\n */\r\nfunction extractExports(content: string, ext: string): ExportInfo[] {\r\n const exports: ExportInfo[] = [];\r\n\r\n // 命名导出: export function foo() / export const bar / export class Baz\r\n const namedExportRegex = /export\\s+(async\\s+)?function\\s+(\\w+)\\s*\\(([^)]*)\\)(\\s*:\\s*([^{]+))?/g;\r\n let match: RegExpExecArray | null;\r\n\r\n while ((match = namedExportRegex.exec(content)) !== null) {\r\n exports.push({\r\n name: match[2],\r\n kind: 'function',\r\n params: parseParams(match[3]),\r\n returnType: match[5]?.trim(),\r\n isAsync: !!match[1],\r\n });\r\n }\r\n\r\n // export const/let/var\r\n const constExportRegex = /export\\s+(const|let|var)\\s+(\\w+)\\s*(?::\\s*([^{=]+))?\\s*[={]/g;\r\n while ((match = constExportRegex.exec(content)) !== null) {\r\n const typeAnnotation = match[3]?.trim();\r\n const kind: ExportKind = typeAnnotation === 'DefineComponent' || isComponentType(typeAnnotation)\r\n ? 'component'\r\n : 'const';\r\n\r\n exports.push({\r\n name: match[2],\r\n kind,\r\n params: [],\r\n returnType: typeAnnotation,\r\n isAsync: false,\r\n });\r\n }\r\n\r\n // export class\r\n const classExportRegex = /export\\s+class\\s+(\\w+)/g;\r\n while ((match = classExportRegex.exec(content)) !== null) {\r\n exports.push({\r\n name: match[1],\r\n kind: 'class',\r\n params: [],\r\n isAsync: false,\r\n });\r\n }\r\n\r\n // export type/interface\r\n const typeExportRegex = /export\\s+(type|interface)\\s+(\\w+)/g;\r\n while ((match = typeExportRegex.exec(content)) !== null) {\r\n exports.push({\r\n name: match[2],\r\n kind: 'type',\r\n params: [],\r\n isAsync: false,\r\n });\r\n }\r\n\r\n // export default\r\n if (/export\\s+default\\s+/.test(content)) {\r\n // export default function\r\n const defaultFuncMatch = /export\\s+default\\s+(async\\s+)?function\\s+(\\w*)\\s*\\(([^)]*)\\)/.exec(content);\r\n if (defaultFuncMatch) {\r\n exports.push({\r\n name: defaultFuncMatch[2] || 'default',\r\n kind: 'default',\r\n params: parseParams(defaultFuncMatch[3]),\r\n isAsync: !!defaultFuncMatch[1],\r\n });\r\n } else {\r\n // export default expression\r\n exports.push({\r\n name: 'default',\r\n kind: ext === '.vue' ? 'component' : 'default',\r\n params: [],\r\n isAsync: false,\r\n });\r\n }\r\n }\r\n\r\n // re-export: export { foo, bar } from './module'\r\n // 这些我们不需要生成测试\r\n\r\n return exports;\r\n}\r\n\r\n/**\r\n * 解析函数参数字符串\r\n */\r\nfunction parseParams(paramsStr: string): string[] {\r\n if (!paramsStr.trim()) return [];\r\n\r\n return paramsStr\r\n .split(',')\r\n .map((p) => {\r\n // 提取参数名(去掉类型注解和默认值)\r\n const trimmed = p.trim();\r\n const nameMatch = trimmed.match(/(?:\\.\\.\\.)?(\\w+)/);\r\n return nameMatch ? nameMatch[1] : trimmed;\r\n })\r\n .filter((p) => p && p !== '_' && !p.startsWith('__'));\r\n}\r\n\r\n/**\r\n * 判断是否为组件类型\r\n */\r\nfunction isComponentType(typeStr?: string): boolean {\r\n if (!typeStr) return false;\r\n return /DefineComponent|FunctionalComponent|Component\\b/i.test(typeStr);\r\n}\r\n\r\n// ─── Vue 组件分析 ────────────────────────────────────────────\r\n\r\n/**\r\n * 分析 Vue 组件\r\n */\r\nfunction analyzeVueComponent(content: string, componentName: string): VueComponentAnalysis {\r\n const analysis: VueComponentAnalysis = {\r\n componentName,\r\n props: [],\r\n emits: [],\r\n methods: [],\r\n computed: [],\r\n usesSetup: false,\r\n };\r\n\r\n // 检测是否使用 <script setup>\r\n analysis.usesSetup = /<script[^>]*setup[^>]*>/.test(content);\r\n\r\n // 提取 <script> 部分内容\r\n const scriptMatch = content.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\r\n if (!scriptMatch) return analysis;\r\n\r\n const scriptContent = scriptMatch[1];\r\n\r\n // 解析 Props\r\n analysis.props = extractProps(scriptContent, analysis.usesSetup);\r\n\r\n // 解析 Emits\r\n analysis.emits = extractEmits(scriptContent, analysis.usesSetup);\r\n\r\n // 解析 Methods(仅 Options API)\r\n if (!analysis.usesSetup) {\r\n analysis.methods = extractMethods(scriptContent);\r\n analysis.computed = extractComputed(scriptContent);\r\n }\r\n\r\n return analysis;\r\n}\r\n\r\n/**\r\n * 提取 Props\r\n */\r\nfunction extractProps(scriptContent: string, usesSetup: boolean): PropInfo[] {\r\n const props: PropInfo[] = [];\r\n\r\n if (usesSetup) {\r\n // defineProps 的对象形式: defineProps({ foo: { type: String, required: true } })\r\n const definePropsObjMatch = scriptContent.match(/defineProps\\s*<[^>]*>\\s*\\(\\s*\\)|defineProps\\s*\\(\\s*\\{([\\s\\S]*?)\\}\\s*\\)/);\r\n if (definePropsObjMatch && definePropsObjMatch[1]) {\r\n const objContent = definePropsObjMatch[1];\r\n // 匹配每个 prop 定义\r\n const propRegex = /(\\w+)\\s*:\\s*\\{([^}]*)\\}/g;\r\n let propMatch: RegExpExecArray | null;\r\n while ((propMatch = propRegex.exec(objContent)) !== null) {\r\n const propBody = propMatch[2];\r\n props.push({\r\n name: propMatch[1],\r\n type: extractPropType(propBody),\r\n required: /required\\s*:\\s*true/.test(propBody),\r\n defaultValue: extractPropDefault(propBody),\r\n });\r\n }\r\n }\r\n\r\n // defineProps 的数组形式: defineProps(['foo', 'bar'])\r\n const definePropsArrMatch = scriptContent.match(/defineProps\\s*\\(\\s*\\[([^\\]]*)\\]\\s*\\)/);\r\n if (definePropsArrMatch && definePropsArrMatch[1]) {\r\n const names = definePropsArrMatch[1].match(/'([^']+)'/g);\r\n if (names) {\r\n for (const name of names) {\r\n const cleanName = name.replace(/'/g, '');\r\n if (!props.some((p) => p.name === cleanName)) {\r\n props.push({\r\n name: cleanName,\r\n type: 'any',\r\n required: false,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // defineProps 的类型形式: defineProps<{ foo: string, bar?: number }>()\r\n const typePropsMatch = scriptContent.match(/defineProps\\s*<\\s*\\{([^}]*)\\}/);\r\n if (typePropsMatch && typePropsMatch[1] && props.length === 0) {\r\n const typeContent = typePropsMatch[1];\r\n const typePropRegex = /(\\??\\s*)(\\w+)\\s*:\\s*([^;,\\n]+)/g;\r\n let typeMatch: RegExpExecArray | null;\r\n while ((typeMatch = typePropRegex.exec(typeContent)) !== null) {\r\n props.push({\r\n name: typeMatch[2],\r\n type: typeMatch[3].trim(),\r\n required: !typeMatch[1].includes('?'),\r\n });\r\n }\r\n }\r\n } else {\r\n // Options API: props: { ... }\r\n const propsObjMatch = scriptContent.match(/props\\s*:\\s*\\{([\\s\\S]*?)\\}\\s*,?\\s*(?:emits|data|computed|methods|setup|components|watch|$)/);\r\n if (propsObjMatch && propsObjMatch[1]) {\r\n const propRegex = /(\\w+)\\s*:\\s*\\{([^}]*)\\}/g;\r\n let propMatch: RegExpExecArray | null;\r\n while ((propMatch = propRegex.exec(propsObjMatch[1])) !== null) {\r\n const propBody = propMatch[2];\r\n props.push({\r\n name: propMatch[1],\r\n type: extractPropType(propBody),\r\n required: /required\\s*:\\s*true/.test(propBody),\r\n defaultValue: extractPropDefault(propBody),\r\n });\r\n }\r\n }\r\n\r\n // Array 形式: props: ['foo', 'bar']\r\n const propsArrMatch = scriptContent.match(/props\\s*:\\s*\\[([^\\]]*)\\]/);\r\n if (propsArrMatch && propsArrMatch[1]) {\r\n const names = propsArrMatch[1].match(/'([^']+)'/g);\r\n if (names) {\r\n for (const name of names) {\r\n const cleanName = name.replace(/'/g, '');\r\n if (!props.some((p) => p.name === cleanName)) {\r\n props.push({\r\n name: cleanName,\r\n type: 'any',\r\n required: false,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return props;\r\n}\r\n\r\n/**\r\n * 提取 prop 类型\r\n */\r\nfunction extractPropType(propBody: string): string {\r\n const typeMatch = propBody.match(/type\\s*:\\s*(\\w+)/);\r\n if (typeMatch) return typeMatch[1];\r\n return 'any';\r\n}\r\n\r\n/**\r\n * 提取 prop 默认值\r\n */\r\nfunction extractPropDefault(propBody: string): string | undefined {\r\n const defaultMatch = propBody.match(/default\\s*:\\s*([^,}\\n]+)/);\r\n if (defaultMatch) return defaultMatch[1].trim();\r\n return undefined;\r\n}\r\n\r\n/**\r\n * 提取 Emits\r\n */\r\nfunction extractEmits(scriptContent: string, usesSetup: boolean): EmitInfo[] {\r\n const emits: EmitInfo[] = [];\r\n\r\n if (usesSetup) {\r\n // defineEmits(['foo', 'bar'])\r\n const arrMatch = scriptContent.match(/defineEmits\\s*\\(\\s*\\[([^\\]]*)\\]\\s*\\)/);\r\n if (arrMatch && arrMatch[1]) {\r\n const names = arrMatch[1].match(/'([^']+)'/g);\r\n if (names) {\r\n for (const name of names) {\r\n emits.push({\r\n name: name.replace(/'/g, ''),\r\n params: [],\r\n });\r\n }\r\n }\r\n }\r\n\r\n // defineEmits<{ foo: [...], bar: [...] }>()\r\n const typeMatch = scriptContent.match(/defineEmits\\s*<\\s*\\{([^}]*)\\}/);\r\n if (typeMatch && typeMatch[1] && emits.length === 0) {\r\n const emitRegex = /(\\w+)\\s*:\\s*\\(([^)]*)\\)/g;\r\n let emitMatch: RegExpExecArray | null;\r\n while ((emitMatch = emitRegex.exec(typeMatch[1])) !== null) {\r\n emits.push({\r\n name: emitMatch[1],\r\n params: parseParams(emitMatch[2]),\r\n });\r\n }\r\n }\r\n } else {\r\n // Options API: emits: ['foo', 'bar'] or emits: { ... }\r\n const arrMatch = scriptContent.match(/emits\\s*:\\s*\\[([^\\]]*)\\]/);\r\n if (arrMatch && arrMatch[1]) {\r\n const names = arrMatch[1].match(/'([^']+)'/g);\r\n if (names) {\r\n for (const name of names) {\r\n emits.push({\r\n name: name.replace(/'/g, ''),\r\n params: [],\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n return emits;\r\n}\r\n\r\n/**\r\n * 提取 Options API 方法\r\n */\r\nfunction extractMethods(scriptContent: string): string[] {\r\n const methods: string[] = [];\r\n const methodsMatch = scriptContent.match(/methods\\s*:\\s*\\{([\\s\\S]*?)\\}\\s*,?\\s*(?:computed|watch|components|mounted|created|setup|$)/);\r\n if (methodsMatch && methodsMatch[1]) {\r\n const methodRegex = /(?:async\\s+)?(\\w+)\\s*\\(/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = methodRegex.exec(methodsMatch[1])) !== null) {\r\n const name = match[1];\r\n if (name !== 'constructor' && !methods.includes(name)) {\r\n methods.push(name);\r\n }\r\n }\r\n }\r\n return methods;\r\n}\r\n\r\n/**\r\n * 提取 computed 属性\r\n */\r\nfunction extractComputed(scriptContent: string): string[] {\r\n const computed: string[] = [];\r\n const computedMatch = scriptContent.match(/computed\\s*:\\s*\\{([\\s\\S]*?)\\}\\s*,?\\s*(?:methods|watch|components|mounted|created|setup|$)/);\r\n if (computedMatch && computedMatch[1]) {\r\n const propRegex = /(\\w+)\\s*(?:\\(\\)|\\{)/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = propRegex.exec(computedMatch[1])) !== null) {\r\n const name = match[1];\r\n if (!computed.includes(name)) {\r\n computed.push(name);\r\n }\r\n }\r\n }\r\n return computed;\r\n}\r\n\r\n// ─── API 调用提取 ────────────────────────────────────────────\r\n\r\n/**\r\n * 从源码中提取 API 调用信息\r\n */\r\nfunction extractAPICalls(content: string, sourceFile: string): APICallInfo[] {\r\n const calls: APICallInfo[] = [];\r\n const seen = new Set<string>();\r\n\r\n // 1. fetch('url', { method: 'POST' }) 或 fetch(`url`)\r\n const fetchRegex = /fetch\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = fetchRegex.exec(content)) !== null) {\r\n const url = cleanTemplateUrl(match[1]);\r\n // 检查同一行附近是否有 method 指定\r\n const afterFetch = content.slice(match.index, match.index + 300);\r\n const methodMatch = afterFetch.match(/method\\s*:\\s*['\"]?(GET|POST|PUT|DELETE|PATCH)['\"]?/i);\r\n const method = methodMatch ? methodMatch[1].toUpperCase() as APICallInfo['method'] : 'GET';\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n // 2. axios.get('url') / axios.post('url') / axios.put / axios.delete / axios.patch\r\n const axiosRegex = /axios\\s*\\.\\s*(get|post|put|delete|patch)\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/gi;\r\n while ((match = axiosRegex.exec(content)) !== null) {\r\n const method = match[1].toUpperCase() as APICallInfo['method'];\r\n const url = cleanTemplateUrl(match[2]);\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n // 3. useFetch('url') / useFetch('url', { method: 'POST' }) (Nuxt/composable)\r\n const useFetchRegex = /useFetch\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/g;\r\n while ((match = useFetchRegex.exec(content)) !== null) {\r\n const url = cleanTemplateUrl(match[1]);\r\n const afterUseFetch = content.slice(match.index, match.index + 300);\r\n const methodMatch = afterUseFetch.match(/method\\s*:\\s*['\"]?(GET|POST|PUT|DELETE|PATCH)['\"]?/i);\r\n const method = methodMatch ? methodMatch[1].toUpperCase() as APICallInfo['method'] : 'GET';\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n // 4. $fetch('url', { method: 'POST' }) (Nuxt)\r\n const dollarFetchRegex = /\\$fetch\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/g;\r\n while ((match = dollarFetchRegex.exec(content)) !== null) {\r\n const url = cleanTemplateUrl(match[1]);\r\n const afterFetch = content.slice(match.index, match.index + 300);\r\n const methodMatch = afterFetch.match(/method\\s*:\\s*['\"]?(GET|POST|PUT|DELETE|PATCH)['\"]?/i);\r\n const method = methodMatch ? methodMatch[1].toUpperCase() as APICallInfo['method'] : 'GET';\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n // 5. httpRequest.get('url') / http.post('url') 等自定义实例\r\n const httpRegex = /\\w+\\s*\\.\\s*(get|post|put|delete|patch)\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/gi;\r\n while ((match = httpRegex.exec(content)) !== null) {\r\n // 排除 axios 已匹配的\r\n const fullLine = content.slice(Math.max(0, match.index - 20), match.index + match[0].length);\r\n if (/axios/i.test(fullLine)) continue;\r\n const method = match[1].toUpperCase() as APICallInfo['method'];\r\n const url = cleanTemplateUrl(match[2]);\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n return calls;\r\n}\r\n\r\n/**\r\n * 清理模板 URL 中的变量插值\r\n * `/api/users/${id}` → `/api/users/:id`\r\n */\r\nfunction cleanTemplateUrl(url: string): string {\r\n return url\r\n .replace(/\\$\\{[^}]*\\}/g, ':param')\r\n .replace(/\\/+/g, '/')\r\n .replace(/:param/g, (_match, offset, str) => {\r\n // 尝试从前面的路径推断参数名\r\n const before = str.slice(Math.max(0, offset - 20), offset);\r\n const nameMatch = before.match(/(\\w+)Id|by(\\w+)|(\\w+)Id/i);\r\n if (nameMatch) {\r\n const name = (nameMatch[1] || nameMatch[2] || nameMatch[3] || 'param').toLowerCase();\r\n return `:${name}`;\r\n }\r\n return ':id';\r\n });\r\n}\r\n\r\n// ─── 扫描目录提取 API ────────────────────────────────────────\r\n\r\n/**\r\n * 扫描项目源码目录,提取所有 API 调用\r\n */\r\nexport function scanAPICalls(srcDir: string): APICallInfo[] {\r\n const absDir = path.resolve(process.cwd(), srcDir);\r\n\r\n if (!fs.existsSync(absDir)) {\r\n return [];\r\n }\r\n\r\n const allCalls: APICallInfo[] = [];\r\n const seen = new Set<string>();\r\n\r\n function walk(dir: string): void {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n for (const entry of entries) {\r\n // 跳过 node_modules, dist, .git 等\r\n if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist') continue;\r\n\r\n const fullPath = path.join(dir, entry.name);\r\n if (entry.isDirectory()) {\r\n walk(fullPath);\r\n } else if (entry.isFile() && /\\.(ts|js|vue|mjs)$/.test(entry.name)) {\r\n try {\r\n const content = fs.readFileSync(fullPath, 'utf-8');\r\n const calls = extractAPICalls(content, path.relative(process.cwd(), fullPath));\r\n for (const call of calls) {\r\n const key = `${call.method}:${call.url}`;\r\n if (!seen.has(key)) {\r\n seen.add(key);\r\n allCalls.push(call);\r\n }\r\n }\r\n } catch {\r\n // 忽略读取失败\r\n }\r\n }\r\n }\r\n }\r\n\r\n walk(absDir);\r\n return allCalls;\r\n}\r\n\r\n/**\r\n * 根据 API 调用信息自动生成 Mock 路由配置\r\n */\r\nexport function generateMockRoutesFromAPICalls(apiCalls: APICallInfo[]): MockRouteCandidate[] {\r\n const routes: MockRouteCandidate[] = [];\r\n\r\n for (const call of apiCalls) {\r\n // 从 URL 路径推断资源名\r\n const segments = call.url.split('/').filter(Boolean);\r\n const resourceName = segments[segments.length - 1]?.replace(/^:/, '') || 'resource';\r\n\r\n let response: Record<string, unknown> | null;\r\n let status = 200;\r\n\r\n switch (call.method) {\r\n case 'GET':\r\n if (call.url.includes(':')) {\r\n // 单个资源\r\n response = { message: 'Success', data: { id: 1, name: `${resourceName}-item` } };\r\n } else {\r\n // 列表\r\n response = { message: 'Success', data: [{ id: 1, name: `${resourceName}-1` }], total: 1 };\r\n }\r\n break;\r\n case 'POST':\r\n status = 201;\r\n response = { message: 'Created', data: { id: 2, name: `{{body.name}}` } };\r\n break;\r\n case 'PUT':\r\n response = { message: 'Updated', data: { id: 1, name: `{{body.name}}` } };\r\n break;\r\n case 'DELETE':\r\n status = 204;\r\n response = null;\r\n break;\r\n case 'PATCH':\r\n response = { message: 'Updated', data: { id: 1, name: `{{body.name}}` } };\r\n break;\r\n default:\r\n response = { message: 'Success' };\r\n }\r\n\r\n routes.push({\r\n method: call.method,\r\n path: call.url,\r\n status,\r\n response,\r\n delay: 100,\r\n sourceFile: call.sourceFile,\r\n });\r\n }\r\n\r\n return routes;\r\n}\r\n\r\n/** 自动生成的 Mock 路由候选项 */\r\nexport interface MockRouteCandidate {\r\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\r\n path: string;\r\n status?: number;\r\n response?: unknown;\r\n delay?: number;\r\n /** 来源文件 */\r\n sourceFile: string;\r\n}\r\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA+EN,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAmIX,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAuBL,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["path","fs","chalk","pathToFileURL","module","import_node_fs","import_node_path","import_node_fs","import_node_path","fs","path","fs","import_node_fs","import_node_path","import_node_url","path","Handlebars","fs","import_node_fs","import_node_path","path","fs","import_node_path","fs","path","import_node_child_process","import_node_path","path","import_node_child_process","import_node_fs","import_node_path","path","fs","module","import_node_fs","import_node_path","fs","pixelmatch","path","import_node_fs","import_node_path","path","fs","exports"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/services/config.ts","../src/services/detector.ts","../src/services/framework-registry.ts","../src/services/template.ts","../src/services/reporter.ts","../src/runners/vitest-runner.ts","../src/runners/playwright-runner.ts","../src/runners/lighthouse-runner.ts","../src/ai/noop-provider.ts","../src/ai/openai-provider.ts","../src/ai/provider.ts","../src/services/mock-server.ts","../src/services/visual.ts","../src/services/source-analyzer.ts"],"sourcesContent":["/**\r\n * QAT - CLI自动化测试工具\r\n * 面向Vue项目,集成Vitest、Playwright,覆盖测试全流程\r\n */\r\n\r\n// 导出类型\r\nexport type {\r\n TestType,\r\n TestStatus,\r\n TestCaseResult,\r\n TestError,\r\n TestSuiteResult,\r\n TestRunResult,\r\n CoverageResult,\r\n PerformanceMetrics,\r\n QATConfig,\r\n GlobalOptions,\r\n RunOptions,\r\n InitOptions,\r\n CreateOptions,\r\n MockOptions,\r\n VisualOptions,\r\n ReportOptions,\r\n SetupOptions,\r\n FrameworkType,\r\n UILibrary,\r\n MonorepoType,\r\n} from './types/index.js';\r\n\r\nexport type {\r\n AICapability,\r\n AIGenerateTestRequest,\r\n AIGenerateTestResponse,\r\n AIAnalyzeResultRequest,\r\n AIAnalyzeResultResponse,\r\n AIProvider,\r\n AIConfig,\r\n SourceAnalysisSummary,\r\n AIPresetProvider,\r\n} from './types/ai.js';\r\n\r\n// 导出配置服务\r\nexport {\r\n loadConfig,\r\n clearConfigCache,\r\n validateConfig,\r\n defineConfig,\r\n generateConfigFile,\r\n writeConfigFile,\r\n DEFAULT_CONFIG,\r\n} from './services/config.js';\r\n\r\n// 导出项目检测\r\nexport {\r\n detectProject,\r\n discoverVueComponents,\r\n discoverUtilityFiles,\r\n} from './services/detector.js';\r\nexport type { ProjectInfo } from './services/detector.js';\r\n\r\n// 导出模板引擎\r\nexport {\r\n renderTemplate,\r\n registerTemplate,\r\n generateTestFile,\r\n} from './services/template.js';\r\nexport type { TemplateContext } from './services/template.js';\r\n\r\n// 导出报告服务\r\nexport {\r\n aggregateResults,\r\n generateHTMLReport,\r\n writeReportToDisk,\r\n} from './services/reporter.js';\r\nexport type { ReportData } from './services/reporter.js';\r\n\r\n// 导出 Runner\r\nexport { runVitest } from './runners/vitest-runner.js';\r\nexport type { VitestRunnerOptions } from './runners/vitest-runner.js';\r\nexport { runPlaywright } from './runners/playwright-runner.js';\r\nexport type { PlaywrightRunnerOptions } from './runners/playwright-runner.js';\r\nexport { runLighthouse } from './runners/lighthouse-runner.js';\r\nexport type { LighthouseRunnerOptions } from './runners/lighthouse-runner.js';\r\n\r\n// 导出 AI Provider 管理\r\nexport {\r\n registerAIProvider,\r\n getRegisteredProviders,\r\n getAIProvider,\r\n resetAIProvider,\r\n isAIAvailable,\r\n testAIConnection,\r\n AI_PRESET_PROVIDERS,\r\n} from './ai/provider.js';\r\nexport type { AIProviderConstructor } from './ai/provider.js';\r\nexport { NoopAIProvider } from './ai/noop-provider.js';\r\nexport { OpenAICompatibleProvider } from './ai/openai-provider.js';\r\n\r\n// 导出 Mock 服务\r\nexport {\r\n getMockServerState,\r\n loadMockRoutes,\r\n startMockServer,\r\n stopMockServer,\r\n createDefaultRoutes,\r\n generateMockRouteTemplate,\r\n initMockRoutesDir,\r\n} from './services/mock-server.js';\r\nexport type { MockRoute, MockServerState } from './services/mock-server.js';\r\n\r\n// 导出视觉回归服务\r\nexport {\r\n compareImages,\r\n createBaseline,\r\n updateBaseline,\r\n updateAllBaselines,\r\n cleanBaselines,\r\n cleanDiffs,\r\n listBaselines,\r\n listDiffs,\r\n compareDirectories,\r\n} from './services/visual.js';\r\nexport type { DiffResult } from './services/visual.js';\r\n\r\n// 导出框架注册表\r\nexport {\r\n registerFramework,\r\n getRegisteredFrameworks,\r\n detectFramework,\r\n detectUILibrary,\r\n detectMonorepo,\r\n discoverAppDirs,\r\n loadExternalFrameworks,\r\n resetRegistry,\r\n} from './services/framework-registry.js';\r\nexport type {\r\n FrameworkDefinition,\r\n FrameworkDetectResult,\r\n DetectContext,\r\n ComponentTestSetup,\r\n LoadExternalFrameworksResult,\r\n} from './services/framework-registry.js';\r\n\r\n// 导出源码分析器\r\nexport {\r\n analyzeFile,\r\n analyzeFiles,\r\n scanAPICalls,\r\n generateMockRoutesFromAPICalls,\r\n} from './services/source-analyzer.js';\r\nexport type {\r\n ExportKind,\r\n ExportInfo,\r\n PropInfo,\r\n EmitInfo,\r\n VueComponentAnalysis,\r\n ModuleAnalysis,\r\n APICallInfo,\r\n MockRouteCandidate,\r\n} from './services/source-analyzer.js';\r\n","/**\r\n * 配置管理服务 - 读取/解析/校验/生成 qat.config.ts\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport chalk from 'chalk';\r\nimport type { QATConfig } from '../types/index.js';\r\n\r\n/** 默认配置 */\r\nexport const DEFAULT_CONFIG: QATConfig = {\r\n project: {\r\n framework: 'vue',\r\n vite: true,\r\n srcDir: 'src',\r\n },\r\n vitest: {\r\n enabled: true,\r\n coverage: true,\r\n globals: true,\r\n environment: 'happy-dom',\r\n },\r\n playwright: {\r\n enabled: true,\r\n browsers: ['chromium'],\r\n baseURL: 'http://localhost:5173',\r\n screenshot: 'only-on-failure',\r\n },\r\n visual: {\r\n enabled: true,\r\n threshold: 0.1,\r\n baselineDir: 'tests/visual/baseline',\r\n diffDir: 'tests/visual/diff',\r\n },\r\n lighthouse: {\r\n enabled: true,\r\n urls: ['http://localhost:5173'],\r\n runs: 3,\r\n thresholds: {\r\n performance: 80,\r\n accessibility: 90,\r\n },\r\n },\r\n mock: {\r\n enabled: true,\r\n port: 3456,\r\n routesDir: 'tests/mock/routes',\r\n },\r\n report: {\r\n outputDir: 'qat-report',\r\n open: false,\r\n },\r\n};\r\n\r\n/** 配置缓存 */\r\nlet cachedConfig: QATConfig | null = null;\r\n\r\n/**\r\n * defineConfig 辅助函数 - 提供类型提示,用于用户 qat.config.ts\r\n */\r\nexport function defineConfig(config: Partial<QATConfig>): Partial<QATConfig> {\r\n return config;\r\n}\r\n\r\n/**\r\n * 自动查找配置文件 (优先 .js,兼容 .ts)\r\n */\r\nfunction findConfigFile(): string {\r\n const cwd = process.cwd();\r\n const jsPath = path.join(cwd, 'qat.config.js');\r\n const tsPath = path.join(cwd, 'qat.config.ts');\r\n if (fs.existsSync(jsPath)) return jsPath;\r\n if (fs.existsSync(tsPath)) return tsPath;\r\n return 'qat.config.js';\r\n}\r\n\r\n/**\r\n * 加载配置文件\r\n * @param configPath 配置文件路径,默认为当前目录下 qat.config.ts\r\n * @param forceReload 强制重新加载(跳过缓存)\r\n */\r\nexport async function loadConfig(configPath?: string, forceReload = false): Promise<QATConfig> {\r\n if (cachedConfig && !forceReload) {\r\n return cachedConfig;\r\n }\r\n\r\n const filePath = configPath || process.env.QAT_CONFIG_PATH || findConfigFile();\r\n\r\n try {\r\n const configFile = await importConfig(filePath);\r\n const config = validateConfig(configFile);\r\n cachedConfig = config;\r\n return config;\r\n } catch (error) {\r\n if (isFileNotFoundError(error)) {\r\n if (process.env.QAT_VERBOSE === 'true') {\r\n console.log(chalk.yellow('未找到配置文件,使用默认配置'));\r\n }\r\n cachedConfig = { ...DEFAULT_CONFIG };\r\n return cachedConfig;\r\n }\r\n throw new Error(`配置文件加载失败: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n}\r\n\r\n/**\r\n * 清除配置缓存\r\n */\r\nexport function clearConfigCache(): void {\r\n cachedConfig = null;\r\n}\r\n\r\n/**\r\n * 动态导入配置文件\r\n */\r\nasync function importConfig(filePath: string): Promise<Partial<QATConfig>> {\r\n const { resolve } = await import('node:path');\r\n const { pathToFileURL } = await import('node:url');\r\n const absolutePath = resolve(process.cwd(), filePath);\r\n\r\n if (!fs.existsSync(absolutePath)) {\r\n throw new Error(`Cannot find module '${absolutePath}'`);\r\n }\r\n\r\n // Windows 兼容:ESM import() 需要 file:// URL,不能直接用绝对路径\r\n const fileUrl = pathToFileURL(absolutePath).href;\r\n\r\n try {\r\n const module = await import(fileUrl);\r\n return module.default || module;\r\n } catch {\r\n // 尝试 .js 扩展名(.ts 文件可能无法直接加载)\r\n const jsPath = absolutePath.replace(/\\.ts$/, '.js');\r\n if (jsPath !== absolutePath && fs.existsSync(jsPath)) {\r\n const jsUrl = pathToFileURL(jsPath).href;\r\n const module = await import(jsUrl);\r\n return module.default || module;\r\n }\r\n throw new Error(`无法加载配置文件: ${absolutePath}`);\r\n }\r\n}\r\n\r\n/**\r\n * 校验配置结构 - 深度合并默认配置并校验字段合法性\r\n */\r\nexport function validateConfig(config: Partial<QATConfig>): QATConfig {\r\n // 深度合并\r\n const merged: QATConfig = {\r\n project: { ...DEFAULT_CONFIG.project, ...config.project },\r\n vitest: { ...DEFAULT_CONFIG.vitest, ...config.vitest },\r\n playwright: { ...DEFAULT_CONFIG.playwright, ...config.playwright },\r\n visual: { ...DEFAULT_CONFIG.visual, ...config.visual },\r\n lighthouse: { ...DEFAULT_CONFIG.lighthouse, ...config.lighthouse },\r\n mock: { ...DEFAULT_CONFIG.mock, ...config.mock },\r\n report: { ...DEFAULT_CONFIG.report, ...config.report },\r\n };\r\n\r\n // AI 配置 - 始终合并,确保存在\r\n if (config.ai) {\r\n merged.ai = {\r\n provider: config.ai.provider || 'openai',\r\n apiKey: config.ai.apiKey,\r\n baseUrl: config.ai.baseUrl,\r\n model: config.ai.model,\r\n };\r\n }\r\n\r\n // 校验必填字段\r\n if (!merged.project.srcDir) {\r\n throw new Error('配置校验失败: project.srcDir 不能为空');\r\n }\r\n\r\n // 校验数值范围\r\n if (merged.visual.threshold < 0 || merged.visual.threshold > 1) {\r\n throw new Error('配置校验失败: visual.threshold 必须在 0-1 之间');\r\n }\r\n\r\n if (merged.lighthouse.runs < 1) {\r\n throw new Error('配置校验失败: lighthouse.runs 不能小于 1');\r\n }\r\n\r\n if (merged.mock.port < 1 || merged.mock.port > 65535) {\r\n throw new Error('配置校验失败: mock.port 必须在 1-65535 之间');\r\n }\r\n\r\n // 校验浏览器列表\r\n const validBrowsers = ['chromium', 'firefox', 'webkit'];\r\n for (const browser of merged.playwright.browsers) {\r\n if (!validBrowsers.includes(browser)) {\r\n throw new Error(`配置校验失败: 不支持的浏览器 \"${browser}\",可选值: ${validBrowsers.join(', ')}`);\r\n }\r\n }\r\n\r\n return merged;\r\n}\r\n\r\n/**\r\n * 生成 qat.config.js 文件内容\r\n * AI 配置存储在 ~/.qat/ai.json,不在此文件中展示\r\n */\r\nexport function generateConfigFile(overrides: Partial<QATConfig> = {}): string {\r\n const config = validateConfig(overrides);\r\n\r\n return `// @ts-check\r\n/**\r\n * QAT 配置文件 - Quick Auto Testing\r\n * 修改后无需重启,下次运行 qat 命令时自动生效\r\n *\r\n * AI 模型配置请运行: qat change\r\n * AI 状态查看请运行: qat status\r\n */\r\nimport { defineConfig } from 'qat-cli';\r\n\r\nexport default defineConfig({\r\n project: {\r\n framework: '${config.project.framework}',\r\n vite: ${config.project.vite ?? true},\r\n srcDir: '${config.project.srcDir}',\r\n },\r\n vitest: {\r\n enabled: ${config.vitest.enabled},\r\n coverage: ${config.vitest.coverage},\r\n globals: ${config.vitest.globals},\r\n environment: '${config.vitest.environment}',\r\n },\r\n playwright: {\r\n enabled: ${config.playwright.enabled},\r\n browsers: [${config.playwright.browsers.map(b => `'${b}'`).join(', ')}],\r\n baseURL: '${config.playwright.baseURL}',\r\n screenshot: '${config.playwright.screenshot}',\r\n },\r\n visual: {\r\n enabled: ${config.visual.enabled},\r\n threshold: ${config.visual.threshold},\r\n baselineDir: '${config.visual.baselineDir}',\r\n diffDir: '${config.visual.diffDir}',\r\n },\r\n lighthouse: {\r\n enabled: ${config.lighthouse.enabled},\r\n urls: [${config.lighthouse.urls.map(u => `'${u}'`).join(', ')}],\r\n runs: ${config.lighthouse.runs},\r\n thresholds: {${Object.entries(config.lighthouse.thresholds)\r\n .map(([k, v]) => `\\n ${k}: ${v},`)\r\n .join('')}\\n },\r\n },\r\n mock: {\r\n enabled: ${config.mock.enabled},\r\n port: ${config.mock.port},\r\n routesDir: '${config.mock.routesDir}',\r\n },\r\n report: {\r\n outputDir: '${config.report.outputDir}',\r\n open: ${config.report.open},\r\n },\r\n});\r\n`;\r\n}\r\n\r\n/**\r\n * 写入配置文件到磁盘\r\n */\r\nexport async function writeConfigFile(\r\n cwd: string,\r\n overrides: Partial<QATConfig> = {},\r\n force = false,\r\n): Promise<string> {\r\n const configPath = path.join(cwd, 'qat.config.js');\r\n\r\n if (fs.existsSync(configPath) && !force) {\r\n throw new Error(`配置文件已存在: ${configPath},使用 --force 覆盖`);\r\n }\r\n\r\n const content = generateConfigFile(overrides);\r\n fs.writeFileSync(configPath, content, 'utf-8');\r\n\r\n return configPath;\r\n}\r\n\r\nfunction isFileNotFoundError(error: unknown): boolean {\r\n if (error instanceof Error) {\r\n return (\r\n error.message.includes('Cannot find') ||\r\n error.message.includes('ENOENT') ||\r\n error.message.includes('无法加载配置文件')\r\n );\r\n }\r\n return false;\r\n}\r\n","/**\r\n * 项目检测服务 - 识别框架版本、依赖、目录结构、组件发现\r\n * 集成框架注册表,支持 Vue / Vben / Nuxt 等框架自动检测\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport type { FrameworkType, UILibrary, MonorepoType } from '../types/index.js';\r\nimport { detectFramework, discoverAppDirs, detectUILibrary, detectMonorepo } from './framework-registry.js';\r\nimport type { ComponentTestSetup } from './framework-registry.js';\r\n\r\nexport interface ProjectInfo {\r\n /** 是否为 Vue 项目 */\r\n isVue: boolean;\r\n /** 是否使用 Vite 构建 */\r\n isVite: boolean;\r\n /** Vue 主版本号 */\r\n vueVersion?: 2 | 3;\r\n /** 是否使用 TypeScript */\r\n typescript: boolean;\r\n /** 源码目录 */\r\n srcDir: string;\r\n /** 包管理器 */\r\n packageManager: 'npm' | 'yarn' | 'pnpm' | 'bun';\r\n /** 所有依赖名列表 */\r\n dependencies: string[];\r\n /** 组件目录列表 */\r\n componentDirs: string[];\r\n /** 页面/视图目录列表 */\r\n pageDirs: string[];\r\n /** 是否已有测试目录 */\r\n hasTests: boolean;\r\n /** 已有的测试框架 */\r\n testFrameworks: string[];\r\n /** 项目名称 */\r\n name: string;\r\n /** 检测到的框架类型 */\r\n framework: FrameworkType;\r\n /** 框架显示名称 */\r\n frameworkDisplayName: string;\r\n /** UI 组件库 */\r\n uiLibrary: UILibrary;\r\n /** Monorepo 类型 */\r\n monorepo: MonorepoType;\r\n /** Monorepo 子项目路径 */\r\n appDirs: string[];\r\n /** 框架检测置信度 */\r\n frameworkConfidence: number;\r\n /** 组件测试配置 */\r\n componentTestSetup?: ComponentTestSetup;\r\n}\r\n\r\n/**\r\n * 检测 Vue 版本 — 兼容 Monorepo,从子项目 package.json 中读取\r\n * 优先级:子项目 vue 依赖 > 根目录 vue 依赖\r\n */\r\nfunction detectVueVersion(cwd: string, rootDeps: Record<string, string>): 2 | 3 | null {\r\n // 1. 先尝试根目录\r\n if (rootDeps['vue']) {\r\n const v = parseVueMajorVersion(rootDeps['vue']);\r\n if (v) return v;\r\n }\r\n\r\n // 2. Monorepo: 遍历子项目查找 vue 依赖\r\n const appDirs = discoverAppDirs(cwd);\r\n for (const appDir of appDirs) {\r\n const subPkgPath = path.join(cwd, appDir, 'package.json');\r\n if (fs.existsSync(subPkgPath)) {\r\n try {\r\n const subPkg = JSON.parse(fs.readFileSync(subPkgPath, 'utf-8'));\r\n const subDeps = {\r\n ...(subPkg.dependencies as Record<string, string>),\r\n ...(subPkg.devDependencies as Record<string, string>),\r\n };\r\n if (subDeps['vue']) {\r\n const v = parseVueMajorVersion(subDeps['vue']);\r\n if (v) return v;\r\n }\r\n } catch {\r\n // 解析失败跳过\r\n }\r\n }\r\n }\r\n\r\n // 3. 检查 packages/ 目录下的子包\r\n const packagesPath = path.join(cwd, 'packages');\r\n if (fs.existsSync(packagesPath)) {\r\n try {\r\n const entries = fs.readdirSync(packagesPath, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (!entry.isDirectory()) continue;\r\n const subPkgPath = path.join(packagesPath, entry.name, 'package.json');\r\n if (fs.existsSync(subPkgPath)) {\r\n try {\r\n const subPkg = JSON.parse(fs.readFileSync(subPkgPath, 'utf-8'));\r\n const subDeps = {\r\n ...(subPkg.dependencies as Record<string, string>),\r\n ...(subPkg.devDependencies as Record<string, string>),\r\n };\r\n if (subDeps['vue']) {\r\n const v = parseVueMajorVersion(subDeps['vue']);\r\n if (v) return v;\r\n }\r\n } catch {\r\n // 解析失败跳过\r\n }\r\n }\r\n }\r\n } catch {\r\n // 目录读取失败跳过\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * 从 vue 依赖版本字符串中解析主版本号\r\n */\r\nfunction parseVueMajorVersion(versionStr: string): 2 | 3 | null {\r\n const clean = versionStr.replace(/^[\\^~>=<\\s]+/, '');\r\n // 处理 workspace:* 或 file: 协议 — 无法从版本号判断,返回 null 让后续逻辑推断\r\n if (clean === '*' || clean.startsWith('workspace:') || clean.startsWith('file:')) {\r\n return null;\r\n }\r\n const major = parseInt(clean.split('.')[0], 10);\r\n if (isNaN(major)) return null;\r\n return major >= 3 ? 3 : 2;\r\n}\r\n\r\n/**\r\n * 当版本号无法直接解析时,通过其他线索推断 Vue 主版本号\r\n * - 检查 vue-demi(Vue 2/3 通用库,通常与 Vue 2 组合式 API 一起使用)\r\n * - 检查 @vue/composition-api(仅 Vue 2)\r\n * - 检查 nuxt(Nuxt 3 = Vue 3,Nuxt 2 = Vue 2)\r\n * - 默认推断为 Vue 3(2024+ 新项目的主流)\r\n */\r\nfunction inferVueVersion(cwd: string, allDeps: Record<string, string>): 2 | 3 {\r\n // Nuxt 3 = Vue 3\r\n if (allDeps['nuxt']) {\r\n const nuxtVer = allDeps['nuxt'].replace(/^[\\^~>=<\\s]+/, '');\r\n const major = parseInt(nuxtVer.split('.')[0], 10);\r\n if (!isNaN(major) && major >= 3) return 3;\r\n if (!isNaN(major) && major < 3) return 2;\r\n }\r\n\r\n // @vue/composition-api 仅 Vue 2 使用\r\n if (allDeps['@vue/composition-api']) return 2;\r\n\r\n // vue-demi 的存在暗示可能使用了组合式 API,但不确定版本\r\n // 检查是否有 Vue 3 特有的依赖\r\n const vue3Indicators = [\r\n 'vue-tsc', // Vue 3 TypeScript 支持\r\n '@vue/compiler-sfc', // Vue 3 SFC 编译器\r\n 'unplugin-vue', // Vue 3 Vite 插件\r\n 'vite-plugin-vue', // Vue 3 Vite 插件\r\n ];\r\n for (const dep of vue3Indicators) {\r\n if (allDeps[dep]) return 3;\r\n }\r\n\r\n // 检查 vite.config 文件中是否有 vue 插件\r\n const viteConfigPaths = ['vite.config.ts', 'vite.config.js'];\r\n for (const cfg of viteConfigPaths) {\r\n const cfgPath = path.join(cwd, cfg);\r\n if (fs.existsSync(cfgPath)) {\r\n try {\r\n const content = fs.readFileSync(cfgPath, 'utf-8');\r\n // @vitejs/plugin-vue2 或 vite-plugin-vue2 明确是 Vue 2\r\n if (content.includes('plugin-vue2') || content.includes('vite-plugin-vue2')) return 2;\r\n // @vitejs/plugin-vue 是 Vue 3\r\n if (content.includes('@vitejs/plugin-vue') || content.includes('unplugin-vue')) return 3;\r\n } catch {\r\n // 读取失败跳过\r\n }\r\n }\r\n }\r\n\r\n // 默认推断为 Vue 3\r\n return 3;\r\n}\r\n\r\n/**\r\n * 检测项目信息\r\n */\r\nexport function detectProject(cwd: string = process.cwd()): ProjectInfo {\r\n const info: ProjectInfo = {\r\n isVue: false,\r\n isVite: false,\r\n typescript: false,\r\n srcDir: 'src',\r\n packageManager: 'npm',\r\n dependencies: [],\r\n componentDirs: [],\r\n pageDirs: [],\r\n hasTests: false,\r\n testFrameworks: [],\r\n name: path.basename(cwd),\r\n framework: 'vue',\r\n frameworkDisplayName: 'Vue',\r\n uiLibrary: 'none',\r\n monorepo: 'none',\r\n appDirs: [],\r\n frameworkConfidence: 0,\r\n };\r\n\r\n // 读取 package.json\r\n const pkgPath = path.join(cwd, 'package.json');\r\n let pkg: Record<string, unknown> = {};\r\n let allDeps: Record<string, string> = {};\r\n\r\n if (fs.existsSync(pkgPath)) {\r\n try {\r\n pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\r\n } catch {\r\n // package.json 解析失败,跳过\r\n }\r\n\r\n allDeps = {\r\n ...(pkg.dependencies as Record<string, string>),\r\n ...(pkg.devDependencies as Record<string, string>),\r\n };\r\n\r\n info.dependencies = Object.keys(allDeps);\r\n info.name = (pkg.name as string) || info.name;\r\n\r\n // 检测 Vue(优先从子项目 package.json 读取,兼容 Monorepo)\r\n const vueVersion = detectVueVersion(cwd, allDeps);\r\n if (vueVersion) {\r\n info.isVue = true;\r\n info.vueVersion = vueVersion;\r\n } else if (allDeps['vue']) {\r\n // vue 依赖存在但版本号无法解析(如 workspace:*),仍标记为 Vue 项目\r\n info.isVue = true;\r\n info.vueVersion = inferVueVersion(cwd, allDeps);\r\n } else {\r\n // 无 vue 依赖,尝试从文件系统检测(项目中有 .vue 文件)\r\n info.isVue = detectVueByFileSystem(cwd);\r\n if (info.isVue) {\r\n info.vueVersion = inferVueVersion(cwd, allDeps);\r\n }\r\n }\r\n\r\n // 检测 Vite\r\n info.isVite = !!allDeps['vite'] || fs.existsSync(path.join(cwd, 'vite.config.ts')) || fs.existsSync(path.join(cwd, 'vite.config.js'));\r\n\r\n // 检测 TypeScript\r\n info.typescript =\r\n !!allDeps['typescript'] ||\r\n fs.existsSync(path.join(cwd, 'tsconfig.json')) ||\r\n fs.existsSync(path.join(cwd, 'tsconfig.app.json'));\r\n\r\n // 检测已有测试框架\r\n if (allDeps['vitest']) info.testFrameworks.push('vitest');\r\n if (allDeps['@playwright/test']) info.testFrameworks.push('playwright');\r\n if (allDeps['jest']) info.testFrameworks.push('jest');\r\n if (allDeps['cypress']) info.testFrameworks.push('cypress');\r\n if (allDeps['@vue/test-utils']) info.testFrameworks.push('@vue/test-utils');\r\n }\r\n\r\n // 获取根目录文件列表(用于框架检测)\r\n const rootFiles = getRootFiles(cwd);\r\n\r\n // 使用框架注册表检测\r\n const frameworkResult = detectFramework({\r\n cwd,\r\n dependencies: allDeps,\r\n rootFiles,\r\n });\r\n\r\n if (frameworkResult) {\r\n info.framework = frameworkResult.framework;\r\n info.frameworkDisplayName = frameworkResult.displayName;\r\n info.frameworkConfidence = frameworkResult.confidence;\r\n info.uiLibrary = frameworkResult.uiLibrary;\r\n info.monorepo = frameworkResult.monorepo;\r\n info.appDirs = frameworkResult.appDirs;\r\n info.srcDir = frameworkResult.srcDir;\r\n info.componentTestSetup = frameworkResult.componentTestSetup;\r\n\r\n // 框架注册表返回的目录\r\n info.componentDirs = frameworkResult.componentDirs.filter((d) =>\r\n fs.existsSync(path.join(cwd, d)),\r\n );\r\n info.pageDirs = frameworkResult.pageDirs.filter((d) =>\r\n fs.existsSync(path.join(cwd, d)),\r\n );\r\n\r\n // Vben / Nuxt 本质也是 Vue\r\n if (frameworkResult.framework === 'vben' || frameworkResult.framework === 'nuxt') {\r\n info.isVue = true;\r\n info.vueVersion = 3;\r\n }\r\n\r\n // 框架注册表检测到 Vue 类型时,确保 isVue 被标记\r\n if (frameworkResult.framework === 'vue' && !info.isVue) {\r\n info.isVue = true;\r\n }\r\n\r\n // 框架注册表检测到 Vue 但 vueVersion 未设置时,推断版本\r\n if (info.isVue && !info.vueVersion) {\r\n info.vueVersion = detectVueVersion(cwd, allDeps) || inferVueVersion(cwd, allDeps);\r\n }\r\n } else {\r\n // 回退到原始检测逻辑\r\n info.srcDir = detectSrcDir(cwd);\r\n info.uiLibrary = detectUILibrary(allDeps);\r\n info.monorepo = detectMonorepo(cwd, rootFiles);\r\n info.appDirs = discoverAppDirs(cwd);\r\n info.componentDirs = discoverComponentDirs(cwd, info.srcDir);\r\n info.pageDirs = discoverPageDirs(cwd, info.srcDir);\r\n }\r\n\r\n // 检测已有测试目录\r\n const possibleTestDirs = ['tests', 'test', '__tests__', 'spec'];\r\n for (const dir of possibleTestDirs) {\r\n if (fs.existsSync(path.join(cwd, dir))) {\r\n info.hasTests = true;\r\n break;\r\n }\r\n }\r\n\r\n // 检测包管理器\r\n if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {\r\n info.packageManager = 'pnpm';\r\n } else if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {\r\n info.packageManager = 'yarn';\r\n } else if (fs.existsSync(path.join(cwd, 'bun.lockb'))) {\r\n info.packageManager = 'bun';\r\n }\r\n\r\n return info;\r\n}\r\n\r\n/**\r\n * 通过文件系统检测是否为 Vue 项目\r\n * 当 package.json 中没有 vue 依赖时,检查源码目录中是否存在 .vue 文件\r\n */\r\nfunction detectVueByFileSystem(cwd: string): boolean {\r\n const possibleSrcDirs = ['src', 'lib', 'app'];\r\n for (const srcDir of possibleSrcDirs) {\r\n const srcPath = path.join(cwd, srcDir);\r\n if (fs.existsSync(srcPath)) {\r\n try {\r\n if (hasVueFilesInDir(srcPath, 2)) return true;\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n }\r\n }\r\n\r\n // 也检查根目录下的 components / pages\r\n const rootVueDirs = ['components', 'pages', 'views'];\r\n for (const dir of rootVueDirs) {\r\n const dirPath = path.join(cwd, dir);\r\n if (fs.existsSync(dirPath)) {\r\n try {\r\n if (hasVueFilesInDir(dirPath, 1)) return true;\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * 递归检查目录中是否存在 .vue 文件(限制递归深度防止性能问题)\r\n */\r\nfunction hasVueFilesInDir(dir: string, maxDepth: number): boolean {\r\n if (maxDepth < 0) return false;\r\n try {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (entry.name === 'node_modules' || entry.name === 'dist') continue;\r\n if (entry.isFile() && entry.name.endsWith('.vue')) return true;\r\n if (entry.isDirectory()) {\r\n if (hasVueFilesInDir(path.join(dir, entry.name), maxDepth - 1)) return true;\r\n }\r\n }\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * 获取根目录文件和目录列表\r\n */\r\nfunction getRootFiles(cwd: string): string[] {\r\n try {\r\n return fs.readdirSync(cwd);\r\n } catch {\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * 检测源码目录(回退逻辑)\r\n */\r\nfunction detectSrcDir(cwd: string): string {\r\n const possibleSrcDirs = ['src', 'lib', 'app'];\r\n for (const dir of possibleSrcDirs) {\r\n if (fs.existsSync(path.join(cwd, dir))) {\r\n return dir;\r\n }\r\n }\r\n return 'src';\r\n}\r\n\r\n/**\r\n * 发现 Vue 组件目录(回退逻辑)\r\n */\r\nfunction discoverComponentDirs(cwd: string, srcDir: string): string[] {\r\n const dirs: string[] = [];\r\n const srcPath = path.join(cwd, srcDir);\r\n\r\n if (!fs.existsSync(srcPath)) return dirs;\r\n\r\n const commonDirs = [\r\n 'components',\r\n 'src/components',\r\n 'src/views/components',\r\n 'src/shared/components',\r\n ];\r\n\r\n for (const dir of commonDirs) {\r\n const fullPath = path.join(cwd, dir);\r\n if (fs.existsSync(fullPath)) {\r\n dirs.push(dir);\r\n }\r\n }\r\n\r\n // 递归查找含 .vue 文件的目录\r\n try {\r\n const entries = fs.readdirSync(srcPath, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (entry.isDirectory() && !dirs.includes(`${srcDir}/${entry.name}`)) {\r\n const subDir = path.join(srcPath, entry.name);\r\n const hasVueFiles = fs.readdirSync(subDir).some((f) => f.endsWith('.vue'));\r\n if (hasVueFiles && entry.name !== 'node_modules') {\r\n dirs.push(`${srcDir}/${entry.name}`);\r\n }\r\n }\r\n }\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n\r\n return dirs;\r\n}\r\n\r\n/**\r\n * 发现页面/视图目录(回退逻辑)\r\n */\r\nfunction discoverPageDirs(cwd: string, _srcDir: string): string[] {\r\n const dirs: string[] = [];\r\n const commonDirs = ['pages', 'views', 'src/pages', 'src/views'];\r\n\r\n for (const dir of commonDirs) {\r\n if (fs.existsSync(path.join(cwd, dir))) {\r\n dirs.push(dir);\r\n }\r\n }\r\n\r\n return dirs;\r\n}\r\n\r\n/**\r\n * 发现项目中的 Vue 组件文件\r\n * @returns 组件文件相对路径列表\r\n */\r\nexport function discoverVueComponents(cwd: string, srcDir: string): string[] {\r\n const components: string[] = [];\r\n const searchPaths = [path.join(cwd, srcDir)];\r\n\r\n // Monorepo: 也扫描子项目\r\n const appDirs = discoverAppDirs(cwd);\r\n for (const appDir of appDirs) {\r\n const appSrcPath = path.join(cwd, appDir, 'src');\r\n if (fs.existsSync(appSrcPath)) {\r\n searchPaths.push(appSrcPath);\r\n }\r\n }\r\n\r\n for (const searchPath of searchPaths) {\r\n if (!fs.existsSync(searchPath)) continue;\r\n try {\r\n walkForVueFiles(searchPath, cwd, components);\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n }\r\n\r\n return components;\r\n}\r\n\r\n/**\r\n * 递归查找 .vue 文件\r\n */\r\nfunction walkForVueFiles(dir: string, cwd: string, result: string[]): void {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== 'dist') {\r\n walkForVueFiles(fullPath, cwd, result);\r\n } else if (entry.isFile() && entry.name.endsWith('.vue')) {\r\n result.push(path.relative(cwd, fullPath).replace(/\\\\/g, '/'));\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 发现项目中的工具函数/服务文件\r\n * @returns 文件相对路径列表\r\n */\r\nexport function discoverUtilityFiles(cwd: string, srcDir: string): string[] {\r\n const files: string[] = [];\r\n const searchPaths = [path.join(cwd, srcDir)];\r\n\r\n // Monorepo: 也扫描子项目和共享包\r\n const appDirs = discoverAppDirs(cwd);\r\n for (const appDir of appDirs) {\r\n const appSrcPath = path.join(cwd, appDir, 'src');\r\n if (fs.existsSync(appSrcPath)) {\r\n searchPaths.push(appSrcPath);\r\n }\r\n }\r\n\r\n const utilityPatterns = [\r\n /^(utils|helpers|services|composables|hooks|api|lib)/,\r\n ];\r\n\r\n for (const searchPath of searchPaths) {\r\n if (!fs.existsSync(searchPath)) continue;\r\n try {\r\n walkForUtilityFiles(searchPath, cwd, searchPath, utilityPatterns, files);\r\n } catch {\r\n // 权限不足等情况跳过\r\n }\r\n }\r\n\r\n return files;\r\n}\r\n\r\n/**\r\n * 递归查找工具函数文件\r\n */\r\nfunction walkForUtilityFiles(\r\n dir: string,\r\n cwd: string,\r\n rootSearchPath: string,\r\n patterns: RegExp[],\r\n result: string[],\r\n): void {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n if (entry.isDirectory()) {\r\n if (entry.name !== 'node_modules' && entry.name !== 'dist' && entry.name !== 'components') {\r\n const relativePath = path.relative(rootSearchPath, fullPath).replace(/\\\\/g, '/');\r\n if (patterns.some((p) => p.test(relativePath))) {\r\n walkForUtilityFiles(fullPath, cwd, rootSearchPath, patterns, result);\r\n }\r\n }\r\n } else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.js'))) {\r\n result.push(path.relative(cwd, fullPath).replace(/\\\\/g, '/'));\r\n }\r\n }\r\n}\r\n","/**\r\n * 框架注册表 - 可扩展的框架检测与适配系统\r\n *\r\n * 添加新框架只需调用 registerFramework() 注册即可,\r\n * detector / template / setup 等模块自动适配。\r\n */\r\n\r\nimport type { FrameworkType, UILibrary, MonorepoType } from '../types/index.js';\r\n\r\n/** 框架检测上下文 */\r\nexport interface DetectContext {\r\n /** 项目根目录 */\r\n cwd: string;\r\n /** package.json 中的所有依赖 */\r\n dependencies: Record<string, string>;\r\n /** 项目根目录下的文件/目录列表 */\r\n rootFiles: string[];\r\n}\r\n\r\n/** 框架检测结果 */\r\nexport interface FrameworkDetectResult {\r\n /** 框架类型 */\r\n framework: FrameworkType;\r\n /** 框架显示名称 */\r\n displayName: string;\r\n /** UI 组件库 */\r\n uiLibrary: UILibrary;\r\n /** 是否为 Monorepo */\r\n monorepo: MonorepoType;\r\n /** Monorepo 下的子项目路径列表 */\r\n appDirs: string[];\r\n /** 源码目录(相对于项目根或子项目根) */\r\n srcDir: string;\r\n /** 额外的组件目录 */\r\n componentDirs: string[];\r\n /** 额外的页面/视图目录 */\r\n pageDirs: string[];\r\n /** 检测置信度 0-1,值越高越匹配 */\r\n confidence: number;\r\n /** 组件测试需要的全局插件/注册 */\r\n componentTestSetup?: ComponentTestSetup;\r\n}\r\n\r\n/** 组件测试配置 */\r\nexport interface ComponentTestSetup {\r\n /** 需要全局安装的 Vue 插件 import 语句 */\r\n globalPlugins?: string[];\r\n /** 需要全局注册的组件 stub import 语句 */\r\n globalStubs?: string[];\r\n /** mount 时的额外配置代码片段 */\r\n mountOptions?: string;\r\n /** 额外的 import 语句 */\r\n extraImports?: string[];\r\n}\r\n\r\n/** 框架定义 */\r\nexport interface FrameworkDefinition {\r\n /** 框架标识 */\r\n type: FrameworkType;\r\n /** 框架显示名称 */\r\n displayName: string;\r\n /** 检测函数:返回置信度 0-1,0 表示不匹配 */\r\n detect: (ctx: DetectContext) => number;\r\n /** 框架检测结果补充(detect 返回 > 0 时调用) */\r\n resolve: (ctx: DetectContext) => Omit<FrameworkDetectResult, 'framework' | 'displayName' | 'confidence'>;\r\n}\r\n\r\n// ─── 框架注册表 ───────────────────────────────────────────────\r\n\r\nconst registry: FrameworkDefinition[] = [];\r\n\r\n/**\r\n * 注册框架定义\r\n */\r\nexport function registerFramework(definition: FrameworkDefinition): void {\r\n // 同类型框架替换,否则追加\r\n const idx = registry.findIndex((f) => f.type === definition.type);\r\n if (idx >= 0) {\r\n registry[idx] = definition;\r\n } else {\r\n registry.push(definition);\r\n }\r\n}\r\n\r\n/**\r\n * 获取所有已注册框架\r\n */\r\nexport function getRegisteredFrameworks(): FrameworkDefinition[] {\r\n return [...registry];\r\n}\r\n\r\n/**\r\n * 检测当前项目使用的框架\r\n * 返回置信度最高的框架检测结果\r\n */\r\nexport function detectFramework(ctx: DetectContext): FrameworkDetectResult | null {\r\n let best: { definition: FrameworkDefinition; confidence: number } | null = null;\r\n\r\n for (const definition of registry) {\r\n const confidence = definition.detect(ctx);\r\n if (confidence > 0 && (!best || confidence > best.confidence)) {\r\n best = { definition, confidence };\r\n }\r\n }\r\n\r\n if (!best) return null;\r\n\r\n const resolved = best.definition.resolve(ctx);\r\n return {\r\n framework: best.definition.type,\r\n displayName: best.definition.displayName,\r\n confidence: best.confidence,\r\n ...resolved,\r\n };\r\n}\r\n\r\n// ─── 工具函数 ─────────────────────────────────────────────────\r\n\r\n/**\r\n * 检测 Monorepo 类型\r\n */\r\nexport function detectMonorepo(_cwd: string, rootFiles: string[]): MonorepoType {\r\n // pnpm workspace\r\n if (rootFiles.includes('pnpm-workspace.yaml')) return 'pnpm-workspace';\r\n // Turborepo\r\n if (rootFiles.includes('turbo.json')) return 'turborepo';\r\n // Nx\r\n if (rootFiles.includes('nx.json')) return 'nx';\r\n // Lerna\r\n if (rootFiles.includes('lerna.json')) return 'lerna';\r\n return 'none';\r\n}\r\n\r\n/**\r\n * 检测 UI 组件库\r\n */\r\nexport function detectUILibrary(dependencies: Record<string, string>): UILibrary {\r\n if (dependencies['ant-design-vue']) return 'ant-design-vue';\r\n if (dependencies['element-plus']) return 'element-plus';\r\n if (dependencies['naive-ui']) return 'naive-ui';\r\n if (dependencies['vuetify']) return 'vuetify';\r\n if (dependencies['primevue']) return 'primevue';\r\n return 'none';\r\n}\r\n\r\n/**\r\n * 扫描 Monorepo 下的子项目目录\r\n */\r\nexport function discoverAppDirs(cwd: string): string[] {\r\n const appsPath = join(cwd, 'apps');\r\n const packagesPath = join(cwd, 'packages');\r\n\r\n const dirs: string[] = [];\r\n\r\n for (const base of [appsPath, packagesPath]) {\r\n try {\r\n const entries = fs.readdirSync(base, { withFileTypes: true });\r\n for (const entry of entries) {\r\n if (entry.isDirectory()) {\r\n const subPkgPath = join(base, entry.name, 'package.json');\r\n if (fs.existsSync(subPkgPath)) {\r\n dirs.push(base === appsPath\r\n ? `apps/${entry.name}`\r\n : `packages/${entry.name}`);\r\n }\r\n }\r\n }\r\n } catch {\r\n // 目录不存在或无权限\r\n }\r\n }\r\n\r\n return dirs;\r\n}\r\n\r\n// ─── 内置框架:Vben ────────────────────────────────────────────\r\n\r\nregisterFramework({\r\n type: 'vben',\r\n displayName: 'Vben Admin',\r\n detect(ctx) {\r\n const { dependencies, rootFiles } = ctx;\r\n // Vben 5.x 使用 @vben/cli\r\n if (dependencies['@vben/cli']) return 1.0;\r\n // Vben 5.x workspace 特征\r\n if (\r\n rootFiles.includes('pnpm-workspace.yaml') &&\r\n rootFiles.includes('apps') &&\r\n dependencies['@vben/types']\r\n ) return 0.95;\r\n // Vben 2.x / 3.x 特征(vite + ant-design-vue 的组合不一定是 vben,置信度降低)\r\n if (dependencies['vite'] && dependencies['ant-design-vue'] && dependencies['pinia']) return 0.3;\r\n return 0;\r\n },\r\n resolve(ctx) {\r\n const { cwd, dependencies } = ctx;\r\n const monorepo = detectMonorepo(cwd, ctx.rootFiles);\r\n const uiLibrary = detectUILibrary(dependencies);\r\n const appDirs = discoverAppDirs(cwd);\r\n\r\n // Vben 5.x 默认子项目命名\r\n const defaultAppNames = ['web-antd', 'web-ele', 'web-naive', 'web-en', 'admin'];\r\n let primaryApp = appDirs.find((d) =>\r\n defaultAppNames.some((name) => d.includes(name)),\r\n );\r\n\r\n // 如果没找到默认子项目,取 apps/ 下第一个\r\n if (!primaryApp && appDirs.length > 0) {\r\n primaryApp = appDirs.find((d) => d.startsWith('apps/')) || appDirs[0];\r\n }\r\n\r\n const srcDir = primaryApp ? `${primaryApp}/src` : 'src';\r\n\r\n return {\r\n uiLibrary,\r\n monorepo,\r\n appDirs,\r\n srcDir,\r\n componentDirs: primaryApp\r\n ? [`${primaryApp}/src/views`, `${primaryApp}/src/components`]\r\n : ['src/views', 'src/components'],\r\n pageDirs: primaryApp\r\n ? [`${primaryApp}/src/views`]\r\n : ['src/views'],\r\n componentTestSetup: resolveVbenTestSetup(uiLibrary),\r\n };\r\n },\r\n});\r\n\r\n// ─── 内置框架:Nuxt ────────────────────────────────────────────\r\n\r\nregisterFramework({\r\n type: 'nuxt',\r\n displayName: 'Nuxt',\r\n detect(ctx) {\r\n if (ctx.dependencies['nuxt']) return 1.0;\r\n return 0;\r\n },\r\n resolve(ctx) {\r\n const { cwd, dependencies } = ctx;\r\n const monorepo = detectMonorepo(cwd, ctx.rootFiles);\r\n const uiLibrary = detectUILibrary(dependencies);\r\n\r\n return {\r\n uiLibrary,\r\n monorepo,\r\n appDirs: [],\r\n srcDir: 'src',\r\n componentDirs: ['src/components', 'components', 'src/pages'],\r\n pageDirs: ['src/pages', 'pages'],\r\n componentTestSetup: uiLibrary !== 'none'\r\n ? resolveUITestSetup(uiLibrary)\r\n : undefined,\r\n };\r\n },\r\n});\r\n\r\n// ─── 内置框架:Vue(默认兜底)──────────────────────────────────\r\n\r\nregisterFramework({\r\n type: 'vue',\r\n displayName: 'Vue',\r\n detect(ctx) {\r\n // 只要项目有 vue 依赖就匹配,但置信度最低,作为兜底\r\n if (ctx.dependencies['vue']) return 0.1;\r\n return 0;\r\n },\r\n resolve(ctx) {\r\n const { cwd, dependencies } = ctx;\r\n const monorepo = detectMonorepo(cwd, ctx.rootFiles);\r\n const uiLibrary = detectUILibrary(dependencies);\r\n const appDirs = discoverAppDirs(cwd);\r\n\r\n return {\r\n uiLibrary,\r\n monorepo,\r\n appDirs,\r\n srcDir: 'src',\r\n componentDirs: ['src/components', 'src/views/components', 'src/shared/components'],\r\n pageDirs: ['src/views', 'src/pages'],\r\n componentTestSetup: uiLibrary !== 'none'\r\n ? resolveUITestSetup(uiLibrary)\r\n : undefined,\r\n };\r\n },\r\n});\r\n\r\n// ─── UI 库测试配置解析 ────────────────────────────────────────\r\n\r\nfunction resolveVbenTestSetup(uiLibrary: UILibrary): ComponentTestSetup {\r\n const base = resolveUITestSetup(uiLibrary);\r\n return {\r\n ...base,\r\n extraImports: [\r\n ...(base.extraImports || []),\r\n // Vben 常用的全局注入\r\n `import { createPinia } from 'pinia';`,\r\n ],\r\n globalPlugins: [\r\n ...(base.globalPlugins || []),\r\n 'createPinia()',\r\n ],\r\n };\r\n}\r\n\r\nfunction resolveUITestSetup(uiLibrary: UILibrary): ComponentTestSetup {\r\n switch (uiLibrary) {\r\n case 'ant-design-vue':\r\n return {\r\n extraImports: [`import Antd from 'ant-design-vue';`],\r\n globalPlugins: ['Antd'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [Antd],\r\n },\r\n}`,\r\n };\r\n case 'element-plus':\r\n return {\r\n extraImports: [`import ElementPlus from 'element-plus';`],\r\n globalPlugins: ['ElementPlus'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [ElementPlus],\r\n },\r\n}`,\r\n };\r\n case 'naive-ui':\r\n return {\r\n extraImports: [`import naive from 'naive-ui';`],\r\n globalPlugins: ['naive'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [naive],\r\n },\r\n}`,\r\n };\r\n case 'vuetify':\r\n return {\r\n extraImports: [\r\n `import { createVuetify } from 'vuetify';`,\r\n `import 'vuetify/styles';`,\r\n ],\r\n globalPlugins: ['createVuetify()'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [createVuetify()],\r\n },\r\n}`,\r\n };\r\n case 'primevue':\r\n return {\r\n extraImports: [`import PrimeVue from 'primevue/config';`],\r\n globalPlugins: ['PrimeVue'],\r\n globalStubs: [],\r\n mountOptions: `{\r\n global: {\r\n plugins: [PrimeVue],\r\n },\r\n}`,\r\n };\r\n default:\r\n return {\r\n extraImports: [],\r\n globalPlugins: [],\r\n globalStubs: [],\r\n mountOptions: '{}',\r\n };\r\n }\r\n}\r\n\r\n// ─── 外部扩展加载 ──────────────────────────────────────────────\r\n\r\nimport fs from 'node:fs';\r\nimport { join, extname } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\n\r\n/** 外部扩展文件候选列表(按优先级排序) */\r\nconst EXTERNAL_EXTENSION_FILES = [\r\n 'qat.config.ext.ts',\r\n 'qat.config.ext.js',\r\n 'qat.config.ext.mjs',\r\n 'qat.config.ext.cjs',\r\n];\r\n\r\n/** 外部扩展目录候选 */\r\nconst EXTERNAL_EXTENSION_DIR = 'qat.ext';\r\n\r\n/** 加载结果 */\r\nexport interface LoadExternalFrameworksResult {\r\n /** 成功加载的框架数量 */\r\n loaded: number;\r\n /** 加载的文件列表 */\r\n files: string[];\r\n /** 错误信息 */\r\n errors: { file: string; error: string }[];\r\n}\r\n\r\n/**\r\n * 加载用户项目中的外部扩展文件\r\n *\r\n * 外部扩展文件可以调用以下编程式 API:\r\n * - registerFramework() — 注册自定义框架\r\n * - registerTemplate() — 注册自定义测试模板(需从 qat 导入)\r\n * - registerAIProvider() — 注册自定义 AI Provider(需从 qat 导入)\r\n *\r\n * 扫描规则(按优先级):\r\n * 1. 单文件模式: qat.config.ext.{ts,js,mjs,cjs}\r\n * 2. 目录模式: qat.ext/index.{ts,js,mjs,cjs}\r\n * 3. 目录多文件: qat.ext/*.{ts,js,mjs,cjs} (排除 index)\r\n *\r\n * 安全机制:\r\n * - 静态扫描危险操作(child_process.exec、eval、process.exit 等)\r\n * - 检测到危险操作时跳过加载,除非文件包含 // @qat-security-override\r\n * - 单个文件加载失败不阻断其他文件\r\n *\r\n * @param cwd - 用户项目根目录\r\n * @returns 加载结果统计\r\n */\r\nexport async function loadExternalFrameworks(cwd: string): Promise<LoadExternalFrameworksResult> {\r\n const result: LoadExternalFrameworksResult = {\r\n loaded: 0,\r\n files: [],\r\n errors: [],\r\n };\r\n\r\n // 1. 尝试单文件模式\r\n for (const filename of EXTERNAL_EXTENSION_FILES) {\r\n const filePath = join(cwd, filename);\r\n if (fs.existsSync(filePath)) {\r\n await importAndTrack(filePath, filename, result);\r\n return result; // 找到单文件就返回,不再扫描目录\r\n }\r\n }\r\n\r\n // 2. 尝试目录模式\r\n const dirPath = join(cwd, EXTERNAL_EXTENSION_DIR);\r\n if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {\r\n return result;\r\n }\r\n\r\n // 2a. 尝试 index 入口\r\n const indexNames = ['index.ts', 'index.js', 'index.mjs', 'index.cjs'];\r\n for (const indexName of indexNames) {\r\n const indexPath = join(dirPath, indexName);\r\n if (fs.existsSync(indexPath)) {\r\n await importAndTrack(indexPath, `${EXTERNAL_EXTENSION_DIR}/${indexName}`, result);\r\n return result; // 有 index 就只加载 index\r\n }\r\n }\r\n\r\n // 2b. 加载目录下所有 .ts/.js 文件(按名称排序保证确定性)\r\n const extensions = ['.ts', '.js', '.mjs', '.cjs'];\r\n let entries: string[];\r\n try {\r\n entries = fs.readdirSync(dirPath);\r\n } catch {\r\n return result;\r\n }\r\n\r\n // 排序确保加载顺序稳定\r\n entries.sort();\r\n\r\n for (const entry of entries) {\r\n // 跳过 index(已在上面的逻辑处理)\r\n if (entry.startsWith('index.')) continue;\r\n\r\n const fullPath = join(dirPath, entry);\r\n try {\r\n if (!fs.statSync(fullPath).isFile()) continue;\r\n } catch {\r\n continue;\r\n }\r\n\r\n const ext = extname(entry);\r\n if (!extensions.includes(ext)) continue;\r\n\r\n await importAndTrack(fullPath, `${EXTERNAL_EXTENSION_DIR}/${entry}`, result);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 动态导入单个框架注册文件并跟踪结果\r\n *\r\n * 包含安全检查:扫描文件中的危险操作模式,\r\n * 检测到时跳过加载并记录警告。\r\n */\r\nasync function importAndTrack(\r\n absolutePath: string,\r\n displayPath: string,\r\n result: LoadExternalFrameworksResult,\r\n): Promise<void> {\r\n result.files.push(displayPath);\r\n\r\n try {\r\n // 安全检查:读取文件内容做基本校验\r\n const content = fs.readFileSync(absolutePath, 'utf-8');\r\n\r\n // 允许用户通过注释覆盖安全检查\r\n const hasOverride = content.includes('// @qat-security-override');\r\n\r\n if (!hasOverride) {\r\n // 警告检测:检查是否有明显的危险操作\r\n const dangerPatterns: { pattern: RegExp; label: string }[] = [\r\n { pattern: /child_process\\.(exec|execSync|spawn|spawnSync)\\s*\\(/, label: 'child_process 执行' },\r\n { pattern: /\\beval\\s*\\(/, label: 'eval()' },\r\n { pattern: /require\\s*\\(\\s*['\"]child_process['\"]\\)/, label: 'child_process 引入' },\r\n { pattern: /process\\.exit\\s*\\(/, label: 'process.exit()' },\r\n { pattern: /fs\\.(unlinkSync|rmdirSync|rmSync)\\s*\\(/, label: '危险文件删除操作' },\r\n ];\r\n\r\n for (const { pattern, label } of dangerPatterns) {\r\n if (pattern.test(content)) {\r\n result.errors.push({\r\n file: displayPath,\r\n error: `安全警告: 检测到潜在危险操作 (${label}),已跳过加载。如确认安全,请在文件顶部添加注释 // @qat-security-override`,\r\n });\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // 使用动态 import 加载模块\r\n // 将文件路径转为 file:// URL 以兼容 Windows\r\n const fileUrl = pathToFileURL(absolutePath).href;\r\n await import(fileUrl);\r\n\r\n result.loaded++;\r\n } catch (error) {\r\n const errMsg = error instanceof Error ? error.message : String(error);\r\n result.errors.push({\r\n file: displayPath,\r\n error: errMsg,\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * 重置框架注册表(清空所有已注册的框架)\r\n * 主要用于测试场景\r\n */\r\nexport function resetRegistry(): void {\r\n registry.length = 0;\r\n}\r\n","/**\r\n * 模板引擎 - Handlebars渲染测试用例模板,支持自定义模板注册和Vue版本适配\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { fileURLToPath } from 'node:url';\r\nimport Handlebars from 'handlebars';\r\nimport type { TestType, FrameworkType, UILibrary } from '../types/index.js';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\n/** 导出项信息(简化版,用于模板上下文) */\r\nexport interface TemplateExportInfo {\r\n name: string;\r\n kind: string;\r\n params: string[];\r\n isAsync: boolean;\r\n returnType?: string;\r\n}\r\n\r\n/** Props 信息(简化版,用于模板上下文) */\r\nexport interface TemplatePropInfo {\r\n name: string;\r\n type: string;\r\n required: boolean;\r\n defaultValue?: string;\r\n}\r\n\r\n/** Emits 信息(简化版,用于模板上下文) */\r\nexport interface TemplateEmitInfo {\r\n name: string;\r\n params: string[];\r\n}\r\n\r\n/** 模板变量 */\r\nexport interface TemplateContext {\r\n /** 测试用例名称 */\r\n name: string;\r\n /** camelCase 格式名称(用于 import 变量名) */\r\n camelName?: string;\r\n /** PascalCase 格式名称(用于组件名) */\r\n pascalName?: string;\r\n /** 被测目标文件路径 */\r\n target: string;\r\n /** 前端框架 */\r\n framework: FrameworkType;\r\n /** Vue 主版本 */\r\n vueVersion?: 2 | 3;\r\n /** 是否使用 TypeScript */\r\n typescript?: boolean;\r\n /** UI 组件库 */\r\n uiLibrary?: UILibrary;\r\n /** 额外的导入语句 */\r\n imports?: string[];\r\n /** 组件测试需要的额外导入 */\r\n extraImports?: string[];\r\n /** 组件测试全局插件 */\r\n globalPlugins?: string[];\r\n /** 组件测试全局 stubs */\r\n globalStubs?: string[];\r\n /** mount 配置选项代码片段 */\r\n mountOptions?: string;\r\n /** ── 源码分析结果 ── */\r\n /** 是否有源码分析数据 */\r\n hasAnalysis?: boolean;\r\n /** 导出项列表 */\r\n exports?: TemplateExportInfo[];\r\n /** 函数类型导出 */\r\n functionExports?: TemplateExportInfo[];\r\n /** 非函数类型导出(const/class/component) */\r\n valueExports?: TemplateExportInfo[];\r\n /** Vue 组件 Props */\r\n props?: TemplatePropInfo[];\r\n /** Vue 组件 Emits */\r\n emits?: TemplateEmitInfo[];\r\n /** Vue 组件方法(Options API) */\r\n methods?: string[];\r\n /** Vue 组件 computed(Options API) */\r\n computed?: string[];\r\n /** 是否为 Vue 组件 */\r\n isVueComponent?: boolean;\r\n /** 必填 props */\r\n requiredProps?: TemplatePropInfo[];\r\n /** 有默认值的 props */\r\n optionalProps?: TemplatePropInfo[];\r\n /** 自定义变量 */\r\n [key: string]: unknown;\r\n}\r\n\r\n/** 模板类型到文件名映射 */\r\nconst TEMPLATE_MAP: Record<TestType, string> = {\r\n unit: 'unit-test.hbs',\r\n component: 'component-test.hbs',\r\n e2e: 'e2e-test.hbs',\r\n api: 'api-test.hbs',\r\n visual: 'visual-test.hbs',\r\n performance: 'performance-test.hbs',\r\n};\r\n\r\n/** 自定义模板注册表 */\r\nconst customTemplates = new Map<string, string>();\r\n\r\n// 注册 Handlebars 辅助函数\r\nHandlebars.registerHelper('eq', (a: unknown, b: unknown) => a === b);\r\nHandlebars.registerHelper('neq', (a: unknown, b: unknown) => a !== b);\r\nHandlebars.registerHelper('includes', (arr: unknown[], val: unknown) => Array.isArray(arr) && arr.includes(val));\r\nHandlebars.registerHelper('join', (arr: unknown[], sep: string) => Array.isArray(arr) ? arr.join(sep) : '');\r\nHandlebars.registerHelper('camelCase', (str: string) => toCamelCase(str));\r\nHandlebars.registerHelper('pascalCase', (str: string) => toPascalCase(str));\r\nHandlebars.registerHelper('length', (arr: unknown) => Array.isArray(arr) ? arr.length : 0);\r\nHandlebars.registerHelper('gt', (a: unknown, b: unknown) => Number(a) > Number(b));\r\nHandlebars.registerHelper('and', (...args: unknown[]) => {\r\n args.pop(); // Handlebars options\r\n return args.every(Boolean);\r\n});\r\nHandlebars.registerHelper('or', (...args: unknown[]) => {\r\n args.pop(); // Handlebars options\r\n return args.some(Boolean);\r\n});\r\nHandlebars.registerHelper('propDefaultValue', (prop: TemplatePropInfo) => {\r\n if (!prop.defaultValue) return 'undefined';\r\n const map: Record<string, string> = { String: \"''\", Number: '0', Boolean: 'false', Array: '[]', Object: '{}' };\r\n return map[prop.type] || prop.defaultValue;\r\n});\r\nHandlebars.registerHelper('propTestValue', (prop: TemplatePropInfo) => {\r\n const map: Record<string, string> = {\r\n String: \"'test-value'\",\r\n Number: '42',\r\n Boolean: 'true',\r\n Array: '[1, 2, 3]',\r\n Object: '{ key: \"value\" }',\r\n };\r\n return map[prop.type] || \"'test-value'\";\r\n});\r\n\r\n/**\r\n * 注册自定义模板\r\n * @param type 测试类型\r\n * @param templateContent 模板内容(Handlebars 格式)\r\n */\r\nexport function registerTemplate(type: string, templateContent: string): void {\r\n customTemplates.set(type, templateContent);\r\n}\r\n\r\n/**\r\n * 渲染测试用例模板\r\n */\r\nexport function renderTemplate(type: TestType, context: TemplateContext): string {\r\n const templateContent = loadTemplate(type);\r\n const template = Handlebars.compile(templateContent);\r\n\r\n // 设置默认值(context 中的值优先)\r\n const fullContext: TemplateContext = {\r\n vueVersion: 3,\r\n typescript: true,\r\n imports: [],\r\n extraImports: [],\r\n globalPlugins: [],\r\n globalStubs: [],\r\n mountOptions: '',\r\n hasAnalysis: false,\r\n exports: [],\r\n functionExports: [],\r\n valueExports: [],\r\n props: [],\r\n emits: [],\r\n methods: [],\r\n computed: [],\r\n isVueComponent: false,\r\n requiredProps: [],\r\n optionalProps: [],\r\n ...context,\r\n framework: context.framework || 'vue',\r\n camelName: context.camelName || toCamelCase(context.name),\r\n pascalName: context.pascalName || toPascalCase(context.name),\r\n };\r\n\r\n // 如果有分析数据,派生辅助字段\r\n if (fullContext.exports && fullContext.exports.length > 0) {\r\n fullContext.hasAnalysis = true;\r\n fullContext.functionExports = fullContext.exports.filter(\r\n (e) => e.kind === 'function' || e.kind === 'default',\r\n );\r\n fullContext.valueExports = fullContext.exports.filter(\r\n (e) => e.kind !== 'function' && e.kind !== 'default' && e.kind !== 'type',\r\n );\r\n }\r\n\r\n if (fullContext.props && fullContext.props.length > 0) {\r\n fullContext.isVueComponent = true;\r\n fullContext.requiredProps = fullContext.props.filter((p) => p.required);\r\n fullContext.optionalProps = fullContext.props.filter((p) => !p.required);\r\n }\r\n\r\n if (fullContext.emits && fullContext.emits.length > 0) {\r\n fullContext.isVueComponent = true;\r\n }\r\n\r\n return template(fullContext);\r\n}\r\n\r\n/**\r\n * 加载模板文件\r\n */\r\nfunction loadTemplate(type: TestType): string {\r\n // 优先使用自定义注册的模板\r\n const custom = customTemplates.get(type);\r\n if (custom) return custom;\r\n\r\n // 查找文件系统中的模板\r\n const templateDir = getTemplateDir();\r\n const templateFile = TEMPLATE_MAP[type];\r\n const templatePath = path.join(templateDir, templateFile);\r\n\r\n if (fs.existsSync(templatePath)) {\r\n return fs.readFileSync(templatePath, 'utf-8');\r\n }\r\n\r\n // 使用内置模板\r\n return getBuiltinTemplate(type);\r\n}\r\n\r\n/**\r\n * 获取模板目录\r\n */\r\nfunction getTemplateDir(): string {\r\n // 优先查找项目目录下的 templates\r\n const projectTemplates = path.join(process.cwd(), 'templates');\r\n if (fs.existsSync(projectTemplates)) {\r\n return projectTemplates;\r\n }\r\n // 使用内置模板目录\r\n return path.join(__dirname, '..', 'templates');\r\n}\r\n\r\n/**\r\n * 内置模板 - 适配 Vue 2/3 和 TypeScript\r\n */\r\nfunction getBuiltinTemplate(type: TestType): string {\r\n const templates: Record<TestType, string> = {\r\n unit: `import { describe, it, expect } from 'vitest';\r\n{{#if hasAnalysis}}\r\n{{#each valueExports}}\r\nimport { {{name}} } from '{{../target}}';\r\n{{/each}}\r\n{{#each functionExports}}\r\nimport { {{name}} } from '{{../target}}';\r\n{{/each}}\r\n{{else}}\r\nimport { {{camelName}} } from '{{target}}';\r\n{{/if}}\r\n\r\ndescribe('{{name}}', () => {\r\n{{#if hasAnalysis}}\r\n {{#each functionExports}}\r\n describe('{{name}}()', () => {\r\n {{#if isAsync}}\r\n it('should resolve successfully', async () => {\r\n const result = await {{name}}({{#each params}}{{#unless @first}}, {{/unless}}{{this}}: undefined{{/each}});\r\n expect(result).toBeDefined();\r\n });\r\n {{else}}\r\n it('should return a value', () => {\r\n const result = {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n expect(result).toBeDefined();\r\n });\r\n {{/if}}\r\n {{#if returnType}}\r\n it('should return correct type', () => {\r\n {{#if isAsync}}\r\n const result = await {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n {{else}}\r\n const result = {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n {{/if}}\r\n expect(result).toBeDefined();\r\n });\r\n {{/if}}\r\n {{#if params.length}}\r\n it('should handle edge cases for parameters', () => {\r\n {{#if isAsync}}\r\n const result = await {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n {{else}}\r\n const result = {{name}}({{#each params}}{{#unless @first}}, {{/unless}}undefined as any{{/each}});\r\n {{/if}}\r\n expect(result).toBeDefined();\r\n });\r\n {{/if}}\r\n });\r\n\r\n {{/each}}\r\n {{#each valueExports}}\r\n describe('{{name}}', () => {\r\n it('should be defined', () => {\r\n expect({{name}}).toBeDefined();\r\n });\r\n {{#if eq kind 'const'}}\r\n it('should have expected value', () => {\r\n // Adjust assertion based on actual value\r\n expect({{name}}).toBeDefined();\r\n });\r\n {{/if}}\r\n {{#if eq kind 'class'}}\r\n it('should be instantiable', () => {\r\n const instance = new {{name}}();\r\n expect(instance).toBeInstanceOf({{name}});\r\n });\r\n {{/if}}\r\n });\r\n\r\n {{/each}}\r\n{{else}}\r\n it('should work correctly', () => {\r\n // TODO: add test logic\r\n expect(true).toBe(true);\r\n });\r\n{{/if}}\r\n});\r\n`,\r\n\r\n component: `import { describe, it, expect } from 'vitest';\r\nimport { mount } from '@vue/test-utils';\r\n{{#each extraImports}}\r\n{{this}}\r\n{{/each}}\r\nimport {{pascalName}} from '{{target}}';\r\n\r\ndescribe('{{name}}', () => {\r\n {{#if isVueComponent}}\r\n const mountComponent = (propsData: Record<string, any> = {}) => {\r\n return mount({{pascalName}}, {\r\n {{#if mountOptions}}\r\n ...{{mountOptions}},\r\n {{/if}}\r\n props: propsData,\r\n });\r\n };\r\n\r\n it('renders correctly', () => {\r\n const wrapper = mountComponent({{#if requiredProps}}{\r\n {{#each requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n {{#if requiredProps}}\r\n it('renders with required props', () => {\r\n const wrapper = mountComponent({\r\n {{#each requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n });\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n {{/if}}\r\n {{#if optionalProps}}\r\n it('renders without optional props', () => {\r\n const wrapper = mountComponent({{#if requiredProps}}{\r\n {{#each requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n it('applies optional props correctly', () => {\r\n const wrapper = mountComponent({\r\n {{#each requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n {{#each optionalProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n });\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n {{/if}}\r\n {{#if emits}}\r\n {{#each emits}}\r\n it('emits {{name}} event', async () => {\r\n const wrapper = mountComponent({{#if ../requiredProps}}{\r\n {{#each ../requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n // TODO: trigger action that emits '{{name}}'\r\n // await wrapper.find('button').trigger('click');\r\n // expect(wrapper.emitted('{{name}}')).toBeTruthy();\r\n });\r\n\r\n {{/each}}\r\n {{/if}}\r\n {{#if computed}}\r\n {{#each computed}}\r\n it('computes {{this}} correctly', () => {\r\n const wrapper = mountComponent({{#if ../requiredProps}}{\r\n {{#each ../requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n // TODO: verify computed property '{{this}}'\r\n // expect(wrapper.vm.{{this}}).toBeDefined();\r\n });\r\n\r\n {{/each}}\r\n {{/if}}\r\n {{#if methods}}\r\n {{#each methods}}\r\n it('calls {{this}} method', async () => {\r\n const wrapper = mountComponent({{#if ../requiredProps}}{\r\n {{#each ../requiredProps}}\r\n {{name}}: {{propTestValue this}},\r\n {{/each}}\r\n }{{/if}});\r\n // TODO: test method '{{this}}'\r\n // await wrapper.vm.{{this}}();\r\n // expect(someCondition).toBe(true);\r\n });\r\n\r\n {{/each}}\r\n {{/if}}\r\n {{else}}\r\n it('renders correctly', () => {\r\n const wrapper = mount({{pascalName}}{{#if mountOptions}}, {{mountOptions}}{{/if}});\r\n expect(wrapper.exists()).toBe(true);\r\n });\r\n\r\n it('renders default slot content', () => {\r\n const wrapper = mount({{pascalName}}, {\r\n {{#if mountOptions}}\r\n ...{{mountOptions}},\r\n {{/if}}\r\n slots: {\r\n default: 'Test Content',\r\n },\r\n });\r\n expect(wrapper.text()).toContain('Test Content');\r\n });\r\n\r\n it('handles user interaction', async () => {\r\n const wrapper = mount({{pascalName}}{{#if mountOptions}}, {{mountOptions}}{{/if}});\r\n // TODO: add interaction test\r\n });\r\n {{/if}}\r\n});\r\n`,\r\n\r\n e2e: `import { test, expect } from '@playwright/test';\r\n\r\ntest.describe('{{name}}', () => {\r\n test.beforeEach(async ({ page }) => {\r\n await page.goto('/');\r\n });\r\n\r\n test('should display page correctly', async ({ page }) => {\r\n // TODO: 添加E2E测试断言\r\n await expect(page).toHaveTitle(/.*/);\r\n });\r\n\r\n test('should navigate between pages', async ({ page }) => {\r\n // TODO: 添加导航测试\r\n });\r\n\r\n test('should be responsive', async ({ page }) => {\r\n await page.setViewportSize({ width: 375, height: 667 });\r\n // TODO: 添加移动端断言\r\n });\r\n});\r\n`,\r\n\r\n api: `import { describe, it, expect, beforeAll, afterAll } from 'vitest';\r\n{{#if imports}}\r\n{{#each imports}}\r\nimport {{this}};\r\n{{/each}}\r\n{{/if}}\r\n\r\ndescribe('{{name}} API', () => {\r\n const baseUrl = process.env.API_BASE_URL || 'http://localhost:3456';\r\n\r\n it('should return 200 for GET request', async () => {\r\n const response = await fetch(\\`\\${baseUrl}/api/{{camelName}}\\`);\r\n expect(response.status).toBe(200);\r\n });\r\n\r\n it('should return expected response format', async () => {\r\n const response = await fetch(\\`\\${baseUrl}/api/{{camelName}}\\`);\r\n const data = await response.json();\r\n expect(data).toBeDefined();\r\n });\r\n\r\n it('should handle error responses', async () => {\r\n const response = await fetch(\\`\\${baseUrl}/api/{{camelName}}/nonexistent\\`, {\r\n method: 'GET',\r\n });\r\n expect(response.status).toBeGreaterThanOrEqual(400);\r\n });\r\n});\r\n`,\r\n\r\n visual: `import { test, expect } from '@playwright/test';\r\n\r\ntest.describe('{{name}} visual regression', () => {\r\n test.beforeEach(async ({ page }) => {\r\n await page.goto('/');\r\n });\r\n\r\n test('should match baseline screenshot - desktop', async ({ page }) => {\r\n await expect(page).toHaveScreenshot('{{name}}-desktop.png');\r\n });\r\n\r\n test('should match baseline screenshot - mobile', async ({ page }) => {\r\n await page.setViewportSize({ width: 375, height: 667 });\r\n await expect(page).toHaveScreenshot('{{name}}-mobile.png');\r\n });\r\n});\r\n`,\r\n\r\n performance: `import { test, expect } from '@playwright/test';\r\n\r\ntest.describe('{{name}} performance', () => {\r\n test('should meet Core Web Vitals thresholds', async ({ page }) => {\r\n await page.goto('/');\r\n\r\n // 测量页面加载性能\r\n const performanceMetrics = await page.evaluate(() => {\r\n const [entry] = performance.getEntriesByType('navigation') as PerformanceNavigationTiming[];\r\n return {\r\n domContentLoaded: entry?.domContentLoadedEventEnd - entry?.domContentLoadedEventStart ?? 0,\r\n loadComplete: entry?.loadEventEnd - entry?.loadEventStart ?? 0,\r\n domInteractive: entry?.domInteractive ?? 0,\r\n };\r\n });\r\n\r\n // DOM 内容加载应在 2s 内\r\n expect(performanceMetrics.domInteractive).toBeLessThan(2000);\r\n });\r\n\r\n test('should not have memory leaks', async ({ page }) => {\r\n await page.goto('/');\r\n\r\n const metrics = await page.metrics();\r\n expect(metrics.JSHeapUsedSize).toBeLessThan(50 * 1024 * 1024); // 50MB\r\n });\r\n});\r\n`,\r\n };\r\n\r\n return templates[type];\r\n}\r\n\r\n/**\r\n * 生成测试文件到磁盘\r\n * @returns 生成的文件路径\r\n */\r\nexport function generateTestFile(\r\n type: TestType,\r\n context: TemplateContext,\r\n outputDir: string,\r\n): string {\r\n const content = renderTemplate(type, context);\r\n\r\n // 根据类型确定输出子目录\r\n const subDirMap: Record<TestType, string> = {\r\n unit: 'unit',\r\n component: 'component',\r\n e2e: 'e2e',\r\n api: 'api',\r\n visual: 'visual',\r\n performance: 'performance',\r\n };\r\n\r\n const subDir = subDirMap[type];\r\n const dir = path.join(outputDir, subDir);\r\n\r\n // 确保目录存在\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n // 生成文件名\r\n const fileName = `${context.name}.${type === 'e2e' ? 'spec' : 'test'}.ts`;\r\n const filePath = path.join(dir, fileName);\r\n\r\n // 检查文件是否已存在\r\n if (fs.existsSync(filePath)) {\r\n throw new Error(`测试文件已存在: ${filePath}`);\r\n }\r\n\r\n fs.writeFileSync(filePath, content, 'utf-8');\r\n return filePath;\r\n}\r\n\r\n/**\r\n * Convert string to camelCase\r\n */\r\nfunction toCamelCase(str: string): string {\r\n return str\r\n .replace(/[-_\\s]+(.)?/g, (_, c: string) => (c ? c.toUpperCase() : ''))\r\n .replace(/^[A-Z]/, (c) => c.toLowerCase());\r\n}\r\n\r\n/**\r\n * Convert string to PascalCase\r\n */\r\nfunction toPascalCase(str: string): string {\r\n const camel = toCamelCase(str);\r\n return camel.charAt(0).toUpperCase() + camel.slice(1);\r\n}\r\n","/**\r\n * 报告聚合服务 - 收集各运行器结果,生成 Markdown 报告\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport type { TestRunResult, CoverageResult } from '../types/index.js';\r\n\r\n/** 报告数据 */\r\nexport interface ReportData {\r\n timestamp: number;\r\n duration: number;\r\n results: TestRunResult[];\r\n summary: {\r\n total: number;\r\n passed: number;\r\n failed: number;\r\n skipped: number;\r\n pending: number;\r\n };\r\n /** 按测试类型分组统计 */\r\n byType: Record<string, { total: number; passed: number; failed: number; skipped: number }>;\r\n /** 覆盖率汇总 */\r\n coverage?: CoverageResult;\r\n /** AI 审计报告 */\r\n reviewSummary?: {\r\n total: number;\r\n approved: number;\r\n avgScore: number;\r\n };\r\n}\r\n\r\n/**\r\n * 聚合多个测试运行结果\r\n */\r\nexport function aggregateResults(results: TestRunResult[]): ReportData {\r\n const summary = {\r\n total: 0,\r\n passed: 0,\r\n failed: 0,\r\n skipped: 0,\r\n pending: 0,\r\n };\r\n\r\n const byType: Record<string, { total: number; passed: number; failed: number; skipped: number }> = {};\r\n let coverage: CoverageResult | undefined;\r\n\r\n for (const result of results) {\r\n const typeKey = result.type;\r\n if (!byType[typeKey]) {\r\n byType[typeKey] = { total: 0, passed: 0, failed: 0, skipped: 0 };\r\n }\r\n\r\n for (const suite of result.suites) {\r\n for (const test of suite.tests) {\r\n summary.total++;\r\n byType[typeKey].total++;\r\n\r\n if (test.status === 'passed') {\r\n summary.passed++;\r\n byType[typeKey].passed++;\r\n } else if (test.status === 'failed') {\r\n summary.failed++;\r\n byType[typeKey].failed++;\r\n } else if (test.status === 'skipped') {\r\n summary.skipped++;\r\n byType[typeKey].skipped++;\r\n } else {\r\n summary.pending++;\r\n }\r\n }\r\n }\r\n\r\n // 合并覆盖率\r\n if (result.coverage) {\r\n if (!coverage) {\r\n coverage = { ...result.coverage };\r\n } else {\r\n // 取最大值(多次运行的覆盖率取最好的)\r\n coverage.lines = Math.max(coverage.lines, result.coverage.lines);\r\n coverage.statements = Math.max(coverage.statements, result.coverage.statements);\r\n coverage.functions = Math.max(coverage.functions, result.coverage.functions);\r\n coverage.branches = Math.max(coverage.branches, result.coverage.branches);\r\n }\r\n }\r\n }\r\n\r\n const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);\r\n\r\n return {\r\n timestamp: Date.now(),\r\n duration: totalDuration,\r\n results,\r\n summary,\r\n byType,\r\n coverage: coverage?.lines ? coverage : undefined,\r\n };\r\n}\r\n\r\n/**\r\n * 格式化耗时\r\n */\r\nfunction formatDuration(ms: number): string {\r\n if (ms < 1000) return `${ms}ms`;\r\n if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;\r\n const minutes = Math.floor(ms / 60000);\r\n const seconds = ((ms % 60000) / 1000).toFixed(0);\r\n return `${minutes}m ${seconds}s`;\r\n}\r\n\r\n/**\r\n * 格式化时间戳\r\n */\r\nfunction formatTimestamp(ts: number): string {\r\n return new Date(ts).toLocaleString('zh-CN', {\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit',\r\n });\r\n}\r\n\r\n/**\r\n * 格式化覆盖率为百分比\r\n */\r\nfunction pct(value: number): string {\r\n return `${(value * 100).toFixed(1)}%`;\r\n}\r\n\r\n/**\r\n * 生成覆盖率 Markdown 表格\r\n */\r\nfunction renderCoverageMD(coverage: CoverageResult): string {\r\n return `\r\n### 覆盖率\r\n\r\n| 指标 | 覆盖率 | 进度 |\r\n|------|--------|------|\r\n| 语句 (Statements) | ${pct(coverage.statements)} | ${renderProgressBar(coverage.statements)} |\r\n| 分支 (Branches) | ${pct(coverage.branches)} | ${renderProgressBar(coverage.branches)} |\r\n| 函数 (Functions) | ${pct(coverage.functions)} | ${renderProgressBar(coverage.functions)} |\r\n| 行 (Lines) | ${pct(coverage.lines)} | ${renderProgressBar(coverage.lines)} |\r\n`;\r\n}\r\n\r\n/**\r\n * 渲染简易进度条(文本形式)\r\n */\r\nfunction renderProgressBar(value: number): string {\r\n const filled = Math.round(value * 10);\r\n const empty = 10 - filled;\r\n return `${'█'.repeat(filled)}${'░'.repeat(empty)}`;\r\n}\r\n\r\n/**\r\n * 测试类型的中文标签\r\n */\r\nconst TYPE_LABELS: Record<string, string> = {\r\n unit: '单元测试',\r\n component: '组件测试',\r\n e2e: 'E2E 测试',\r\n api: 'API 测试',\r\n visual: '视觉回归测试',\r\n performance: '性能测试',\r\n};\r\n\r\n/**\r\n * 生成完整 Markdown 报告\r\n */\r\nexport function generateMDReport(data: ReportData): string {\r\n const passRate = data.summary.total > 0\r\n ? ((data.summary.passed / data.summary.total) * 100).toFixed(1)\r\n : '0';\r\n\r\n const rateIcon = parseFloat(passRate) >= 80 ? '✅' : parseFloat(passRate) >= 50 ? '⚠️' : '❌';\r\n\r\n const lines: string[] = [];\r\n\r\n // 标题\r\n lines.push(`# QAT 测试报告`);\r\n lines.push('');\r\n lines.push(`> 生成时间: ${formatTimestamp(data.timestamp)} | 总耗时: ${formatDuration(data.duration)}`);\r\n lines.push('');\r\n\r\n // 总览\r\n lines.push(`## 总览`);\r\n lines.push('');\r\n lines.push(`| 指标 | 数值 |`);\r\n lines.push(`|------|------|`);\r\n lines.push(`| 通过率 | ${rateIcon} **${passRate}%** |`);\r\n lines.push(`| 总用例 | ${data.summary.total} |`);\r\n lines.push(`| ✅ 通过 | ${data.summary.passed} |`);\r\n if (data.summary.failed > 0) lines.push(`| ❌ 失败 | ${data.summary.failed} |`);\r\n if (data.summary.skipped > 0) lines.push(`| ⏭️ 跳过 | ${data.summary.skipped} |`);\r\n if (data.summary.pending > 0) lines.push(`| ⏳ 待定 | ${data.summary.pending} |`);\r\n lines.push(`| ⏱️ 耗时 | ${formatDuration(data.duration)} |`);\r\n lines.push('');\r\n\r\n // 按类型统计\r\n if (Object.keys(data.byType).length > 0) {\r\n lines.push(`## 按类型统计`);\r\n lines.push('');\r\n lines.push(`| 类型 | 通过 | 失败 | 跳过 | 总计 | 通过率 |`);\r\n lines.push(`|------|------|------|------|------|--------|`);\r\n\r\n for (const [type, stats] of Object.entries(data.byType)) {\r\n const label = TYPE_LABELS[type] || type;\r\n const rate = stats.total > 0 ? ((stats.passed / stats.total) * 100).toFixed(0) + '%' : '-';\r\n lines.push(`| ${label} | ${stats.passed} | ${stats.failed} | ${stats.skipped} | ${stats.total} | ${rate} |`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n // 覆盖率\r\n if (data.coverage) {\r\n lines.push(renderCoverageMD(data.coverage));\r\n lines.push('');\r\n }\r\n\r\n // 测试详情\r\n lines.push(`## 测试详情`);\r\n lines.push('');\r\n\r\n for (const result of data.results) {\r\n const typeLabel = TYPE_LABELS[result.type] || result.type;\r\n const statusIcon = result.status === 'passed' ? '✅' : result.status === 'failed' ? '❌' : '⚠️';\r\n\r\n lines.push(`### ${statusIcon} ${typeLabel}`);\r\n lines.push('');\r\n\r\n if (result.suites.length === 0) {\r\n lines.push(`*无测试结果*`);\r\n lines.push('');\r\n continue;\r\n }\r\n\r\n for (const suite of result.suites) {\r\n const suiteIcon = suite.status === 'passed' ? '✅' : suite.status === 'failed' ? '❌' : '⚠️';\r\n lines.push(`#### ${suiteIcon} ${suite.name}`);\r\n lines.push('');\r\n lines.push(`- 文件: \\`${suite.file}\\``);\r\n lines.push(`- 耗时: ${formatDuration(suite.duration)}`);\r\n lines.push('');\r\n\r\n if (suite.tests.length > 0) {\r\n lines.push(`| 状态 | 测试名称 | 耗时 |`);\r\n lines.push(`|------|----------|------|`);\r\n\r\n for (const test of suite.tests) {\r\n const testIcon = test.status === 'passed' ? '✅' : test.status === 'failed' ? '❌' : test.status === 'skipped' ? '⏭️' : '⏳';\r\n const name = test.error ? `**${test.name}**` : test.name;\r\n lines.push(`| ${testIcon} | ${name} | ${formatDuration(test.duration)} |`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n // 失败详情\r\n const failedTests = suite.tests.filter((t) => t.status === 'failed' && t.error);\r\n if (failedTests.length > 0) {\r\n lines.push(`<details>`);\r\n lines.push(`<summary>❌ 失败详情 (${failedTests.length})</summary>`);\r\n lines.push('');\r\n\r\n for (const test of failedTests) {\r\n lines.push(`**${test.name}**`);\r\n lines.push('```');\r\n lines.push(test.error!.message);\r\n if (test.error!.stack) {\r\n lines.push(test.error!.stack);\r\n }\r\n if (test.error!.expected && test.error!.actual) {\r\n lines.push(`Expected: ${test.error!.expected}`);\r\n lines.push(`Actual: ${test.error!.actual}`);\r\n }\r\n lines.push('```');\r\n lines.push('');\r\n }\r\n\r\n lines.push(`</details>`);\r\n lines.push('');\r\n }\r\n }\r\n }\r\n\r\n // 页脚\r\n lines.push('---');\r\n lines.push('');\r\n lines.push(`*由 QAT 自动化测试工具生成 | ${formatTimestamp(data.timestamp)}*`);\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n/**\r\n * 将报告写入磁盘(Markdown 格式)\r\n * @returns 报告文件路径\r\n */\r\nexport function writeReportToDisk(data: ReportData, outputDir: string): string {\r\n const md = generateMDReport(data);\r\n const dir = path.resolve(outputDir);\r\n\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n const mdPath = path.join(dir, 'report.md');\r\n fs.writeFileSync(mdPath, md, 'utf-8');\r\n\r\n // 同时写入 JSON 格式的原始数据,供程序读取\r\n const jsonPath = path.join(dir, 'report.json');\r\n fs.writeFileSync(jsonPath, JSON.stringify(data, null, 2), 'utf-8');\r\n\r\n return mdPath;\r\n}\r\n\r\n/**\r\n * 生成 HTML 报告(保留兼容,从 Markdown 转换为简易 HTML)\r\n */\r\nexport function generateHTMLReport(data: ReportData): string {\r\n const passRate = data.summary.total > 0\r\n ? ((data.summary.passed / data.summary.total) * 100).toFixed(1)\r\n : '0';\r\n\r\n const md = generateMDReport(data);\r\n\r\n // 简易 Markdown → HTML 转换\r\n const html = md\r\n .replace(/^### (.+)$/gm, '<h3>$1</h3>')\r\n .replace(/^## (.+)$/gm, '<h2>$1</h2>')\r\n .replace(/^# (.+)$/gm, '<h1>$1</h1>')\r\n .replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>')\r\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\r\n .replace(/`([^`]+)`/g, '<code>$1</code>')\r\n .replace(/^---$/gm, '<hr>')\r\n .replace(/\\|(.+)\\|/g, (match) => {\r\n const cells = match.split('|').filter(Boolean).map((c: string) => c.trim());\r\n if (cells.every((c: string) => c.startsWith('-') || c === '')) return '';\r\n const tds = cells.map((c: string) => `<td>${c}</td>`).join('');\r\n return `<tr>${tds}</tr>`;\r\n })\r\n .replace(/<tr>/g, '<table><tr>')\r\n .replace(/<\\/tr>(?!\\s*<table>)/g, '</tr></table>')\r\n .replace(/<\\/table>\\s*<table>/g, '')\r\n .replace(/^✅/gm, '<span style=\"color:#22c55e\">✅</span>')\r\n .replace(/^❌/gm, '<span style=\"color:#ef4444\">❌</span>')\r\n .replace(/^⚠️/gm, '<span style=\"color:#f59e0b\">⚠️</span>');\r\n\r\n return `<!DOCTYPE html>\r\n<html lang=\"zh-CN\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>QAT 测试报告 - ${formatTimestamp(data.timestamp)}</title>\r\n <style>\r\n body {\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\r\n max-width: 960px; margin: 0 auto; padding: 20px;\r\n background: #f8fafc; color: #1e293b; line-height: 1.6;\r\n }\r\n h1 { border-bottom: 2px solid #3b82f6; padding-bottom: 8px; }\r\n h2 { margin-top: 24px; color: #1e40af; }\r\n h3 { margin-top: 16px; color: #334155; }\r\n h4 { margin-top: 12px; color: #475569; }\r\n blockquote { color: #64748b; border-left: 3px solid #cbd5e1; padding-left: 12px; margin: 8px 0; }\r\n table { border-collapse: collapse; width: 100%; margin: 12px 0; }\r\n td, th { border: 1px solid #e2e8f0; padding: 8px 12px; text-align: left; }\r\n tr:nth-child(even) { background: #f1f5f9; }\r\n code { background: #e2e8f0; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }\r\n pre { background: #1e293b; color: #e2e8f0; padding: 16px; border-radius: 8px; overflow-x: auto; }\r\n pre code { background: none; padding: 0; }\r\n hr { border: none; border-top: 1px solid #e2e8f0; margin: 24px 0; }\r\n details { margin: 8px 0; }\r\n summary { cursor: pointer; color: #ef4444; font-weight: 600; }\r\n strong { color: #1e293b; }\r\n .pass-rate { font-size: 48px; font-weight: 700; text-align: center; margin: 20px 0;\r\n color: ${parseFloat(passRate) >= 80 ? '#22c55e' : parseFloat(passRate) >= 50 ? '#f59e0b' : '#ef4444'};\r\n }\r\n .pass-rate-label { text-align: center; color: #64748b; margin-bottom: 20px; }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"pass-rate\">${passRate}%</div>\r\n <div class=\"pass-rate-label\">测试通过率</div>\r\n ${html}\r\n</body>\r\n</html>`;\r\n}\r\n","/**\r\n * Vitest 运行器 - 通过子进程调用Vitest,解析输出收集结果\r\n */\r\n\r\nimport { execFile } from 'node:child_process';\r\nimport path from 'node:path';\r\nimport fs from 'node:fs';\r\nimport os from 'node:os';\r\nimport type { TestRunResult, TestSuiteResult, TestCaseResult, TestStatus, TestType, CoverageResult } from '../types/index.js';\r\n\r\nexport interface VitestRunnerOptions {\r\n type: TestType;\r\n files?: string[];\r\n watch?: boolean;\r\n coverage?: boolean;\r\n configPath?: string;\r\n /** 额外传递给 vitest 的参数 */\r\n extraArgs?: string[];\r\n}\r\n\r\nconst isVerbose = () => process.env.QAT_VERBOSE === 'true';\r\n\r\nfunction debug(label: string, ...args: unknown[]): void {\r\n if (isVerbose()) {\r\n console.log(`\\x1b[90m [debug:${label}]\\x1b[0m`, ...args);\r\n }\r\n}\r\n\r\n/**\r\n * 执行Vitest测试\r\n */\r\nexport async function runVitest(options: VitestRunnerOptions): Promise<TestRunResult> {\r\n const startTime = Date.now();\r\n\r\n const args = buildVitestArgs(options);\r\n debug('vitest', '命令参数:', args.join(' '));\r\n\r\n try {\r\n const result = await execVitest(args);\r\n const endTime = Date.now();\r\n\r\n debug('vitest', `解析结果: ${result.suites.length} 个套件, ${result.suites.reduce((s, su) => s + su.tests.length, 0)} 个用例`);\r\n debug('vitest', '解析方式:', result.parseMethod);\r\n\r\n return {\r\n type: options.type,\r\n status: result.success ? 'passed' : 'failed',\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites: result.suites,\r\n coverage: result.coverage,\r\n };\r\n } catch (error) {\r\n const endTime = Date.now();\r\n return {\r\n type: options.type,\r\n status: 'failed',\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites: [{\r\n name: 'Vitest Runner',\r\n file: options.files?.[0] || 'unknown',\r\n type: options.type,\r\n status: 'failed',\r\n duration: endTime - startTime,\r\n tests: [{\r\n name: 'Runner Error',\r\n file: options.files?.[0] || 'unknown',\r\n status: 'failed',\r\n duration: 0,\r\n error: {\r\n message: error instanceof Error ? error.message : String(error),\r\n },\r\n retries: 0,\r\n }],\r\n }],\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * 构建vitest命令参数\r\n */\r\nfunction buildVitestArgs(options: VitestRunnerOptions): string[] {\r\n const args = ['vitest', 'run', '--reporter=json'];\r\n\r\n // 指定测试文件\r\n if (options.files && options.files.length > 0) {\r\n args.push(...options.files);\r\n } else {\r\n // 根据测试类型确定包含路径\r\n const includeMap: Record<string, string[]> = {\r\n unit: ['tests/unit'],\r\n component: ['tests/component'],\r\n api: ['tests/api'],\r\n };\r\n const includes = includeMap[options.type];\r\n if (includes) {\r\n args.push('--include', includes.map((d) => `${d}/**/*.test.ts`).join(','));\r\n }\r\n }\r\n\r\n // 覆盖率\r\n if (options.coverage) {\r\n args.push('--coverage');\r\n }\r\n\r\n // 配置文件\r\n if (options.configPath) {\r\n args.push('--config', options.configPath);\r\n }\r\n\r\n // 额外参数\r\n if (options.extraArgs) {\r\n args.push(...options.extraArgs);\r\n }\r\n\r\n return args;\r\n}\r\n\r\n/**\r\n * 执行结果\r\n */\r\ninterface ExecResult {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n coverage?: CoverageResult;\r\n rawOutput: string;\r\n parseMethod: string;\r\n}\r\n\r\n/**\r\n * 执行vitest命令并解析JSON输出\r\n * 使用 --outputFile 将结果写入临时文件,避免 stdout 混杂日志导致 JSON 解析失败\r\n */\r\nasync function execVitest(args: string[]): Promise<ExecResult> {\r\n const tmpFile = path.join(os.tmpdir(), `qat-vitest-result-${Date.now()}.json`);\r\n\r\n // 添加 outputFile 参数让 vitest 将 JSON 结果写入临时文件\r\n const argsWithOutput = [...args, '--outputFile', tmpFile];\r\n\r\n return new Promise((resolve, reject) => {\r\n const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n debug('vitest', '执行命令:', npx, argsWithOutput.join(' '));\r\n\r\n const child = execFile(npx, argsWithOutput, {\r\n cwd: process.cwd(),\r\n env: { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1' },\r\n maxBuffer: 50 * 1024 * 1024,\r\n shell: true,\r\n }, (error, stdout, stderr) => {\r\n const rawOutput = stdout || stderr || '';\r\n const exitCode = error && 'code' in error ? (error as Error & { code?: number }).code : 0;\r\n\r\n debug('vitest', `退出码: ${exitCode}`);\r\n debug('vitest', `stdout 长度: ${stdout?.length || 0}, stderr 长度: ${stderr?.length || 0}`);\r\n\r\n // 1. 优先从临时文件读取 JSON 结果\r\n let jsonResult: string | null = null;\r\n try {\r\n if (fs.existsSync(tmpFile)) {\r\n jsonResult = fs.readFileSync(tmpFile, 'utf-8');\r\n debug('vitest', `从临时文件读取到 JSON (${jsonResult.length} 字符)`);\r\n debug('vitest', 'JSON 前 500 字符:', jsonResult.substring(0, 500));\r\n fs.unlinkSync(tmpFile);\r\n } else {\r\n debug('vitest', '临时文件不存在:', tmpFile);\r\n }\r\n } catch (e) {\r\n debug('vitest', '临时文件读取失败:', e instanceof Error ? e.message : String(e));\r\n }\r\n\r\n // 2. 解析临时文件的 JSON\r\n if (jsonResult) {\r\n try {\r\n const parsed = parseVitestJSON(jsonResult);\r\n debug('vitest', '从临时文件解析成功:', parsed.suites.length, '个套件');\r\n resolve({ ...parsed, rawOutput, parseMethod: 'outputFile-JSON' });\r\n return;\r\n } catch (e) {\r\n debug('vitest', '临时文件 JSON 解析失败:', e instanceof Error ? e.message : String(e));\r\n }\r\n }\r\n\r\n // 3. 回退:从 stdout 尝试提取 JSON\r\n debug('vitest', '尝试从 stdout 提取 JSON...');\r\n try {\r\n const parsed = parseFromStdout(rawOutput);\r\n debug('vitest', '从 stdout 解析成功:', parsed.suites.length, '个套件');\r\n resolve({ ...parsed, rawOutput, parseMethod: 'stdout-JSON' });\r\n return;\r\n } catch (e) {\r\n debug('vitest', 'stdout JSON 解析失败:', e instanceof Error ? e.message : String(e));\r\n }\r\n\r\n // 4. 最终回退:从文本输出提取结果\r\n debug('vitest', '尝试从文本输出解析...');\r\n if (rawOutput) {\r\n const parsed = parseVitestTextOutput(rawOutput, !!error);\r\n debug('vitest', '文本解析结果:', parsed.suites.length, '个套件');\r\n resolve({ ...parsed, rawOutput, parseMethod: 'text-fallback' });\r\n return;\r\n }\r\n\r\n if (error && error.message.includes('ENOENT')) {\r\n reject(new Error('未找到 vitest,请确保已安装: npm install -D vitest'));\r\n return;\r\n }\r\n\r\n debug('vitest', '所有解析方式均失败,返回空结果');\r\n resolve({ success: !error, suites: [], rawOutput, parseMethod: 'none' });\r\n });\r\n\r\n child.on('error', (err) => {\r\n try { fs.unlinkSync(tmpFile); } catch {}\r\n reject(new Error(`Vitest 执行失败: ${err.message}`));\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 解析 Vitest 的 JSON 输出\r\n * 支持 Vitest 1.x/2.x 的 JSON reporter 格式\r\n * 参考: https://vitest.dev/config/#reporters\r\n */\r\nfunction parseVitestJSON(jsonStr: string): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n coverage?: CoverageResult;\r\n} {\r\n const data = JSON.parse(jsonStr);\r\n const suites: TestSuiteResult[] = [];\r\n\r\n debug('vitest-json', 'JSON 顶层字段:', Object.keys(data).join(', '));\r\n\r\n // ─── Vitest 2.x JSON 格式 ─────────────────────────\r\n // 格式: { testResults: [{ name, assertionResults: [...] }] }\r\n // 或: { testResults: [{ name, tests: [...] }] }\r\n if (data.testResults && Array.isArray(data.testResults)) {\r\n debug('vitest-json', `testResults 数量: ${data.testResults.length}`);\r\n for (const fileResult of data.testResults) {\r\n const suiteTests = parseTestResults(fileResult);\r\n suites.push({\r\n name: path.basename(fileResult.name || 'unknown'),\r\n file: fileResult.name || 'unknown',\r\n type: 'unit' as TestType,\r\n status: mapVitestStatus(fileResult.status),\r\n duration: fileResult.duration || 0,\r\n tests: suiteTests,\r\n });\r\n }\r\n }\r\n\r\n // ─── Vitest 2.x 新格式(扁平结构)─────────────────\r\n // 格式: { numTotalTests, numPassedTests, numFailedTests, numPendingTests, ... }\r\n // 可能没有 testResults,但有汇总数据\r\n if (suites.length === 0 && data.numTotalTests !== undefined) {\r\n debug('vitest-json', `使用汇总格式: total=${data.numTotalTests}, passed=${data.numPassedTests}`);\r\n suites.push({\r\n name: 'Vitest Results',\r\n file: 'unknown',\r\n type: 'unit' as TestType,\r\n status: data.numFailedTests > 0 ? 'failed' : 'passed',\r\n duration: 0,\r\n tests: buildTestsFromSummary(data),\r\n });\r\n }\r\n\r\n // ─── Vitest 嵌套格式(suites 结构)─────────────────\r\n // 有些版本输出: { suites: [{ name, tests: [...] }] }\r\n if (suites.length === 0 && data.suites && Array.isArray(data.suites)) {\r\n debug('vitest-json', `suites 数量: ${data.suites.length}`);\r\n for (const suiteData of data.suites) {\r\n const suiteTests: TestCaseResult[] = [];\r\n for (const test of (suiteData.tests || [])) {\r\n suiteTests.push({\r\n name: test.name || test.title || 'unknown',\r\n file: test.file || suiteData.file || 'unknown',\r\n status: mapVitestStatus(test.status || test.result?.status),\r\n duration: test.duration || test.result?.duration || 0,\r\n error: test.result?.errors?.[0]\r\n ? { message: test.result.errors[0].message || String(test.result.errors[0]) }\r\n : undefined,\r\n retries: 0,\r\n });\r\n }\r\n suites.push({\r\n name: suiteData.name || 'unknown',\r\n file: suiteData.file || 'unknown',\r\n type: 'unit' as TestType,\r\n status: suiteTests.some((t) => t.status === 'failed') ? 'failed' : 'passed',\r\n duration: 0,\r\n tests: suiteTests,\r\n });\r\n }\r\n }\r\n\r\n // ─── 覆盖率解析 ──────────────────────────────\r\n let coverage: CoverageResult | undefined;\r\n if (data.coverageMap) {\r\n coverage = extractCoverage(data.coverageMap);\r\n }\r\n // Vitest 2.x 覆盖率格式\r\n if (!coverage && data.coverage && typeof data.coverage === 'object') {\r\n const cov = data.coverage as Record<string, unknown>;\r\n const totals = (cov.totals || cov) as Record<string, unknown>;\r\n const getVal = (key: string): number => {\r\n const v = totals[key];\r\n return typeof v === 'number' ? v : (typeof v === 'object' && v !== null && 'pct' in (v as Record<string, unknown>)) ? ((v as Record<string, unknown>).pct as number) / 100 : 0;\r\n };\r\n coverage = {\r\n lines: getVal('lines'),\r\n statements: getVal('statements'),\r\n functions: getVal('functions'),\r\n branches: getVal('branches'),\r\n };\r\n }\r\n\r\n const success = data.success !== false\r\n ? (data.numFailedTests !== undefined ? data.numFailedTests === 0 : suites.every((s) => s.status !== 'failed'))\r\n : false;\r\n\r\n return { success, suites, coverage };\r\n}\r\n\r\n/**\r\n * 从 fileResult 中解析测试用例\r\n */\r\nfunction parseTestResults(fileResult: Record<string, unknown>): TestCaseResult[] {\r\n const tests: TestCaseResult[] = [];\r\n\r\n // assertionResults (Jest 兼容格式)\r\n const assertions = fileResult.assertionResults || fileResult.tests || [];\r\n for (const assertion of assertions as Record<string, unknown>[]) {\r\n tests.push({\r\n name: (assertion.title || assertion.fullName || assertion.name || 'unknown') as string,\r\n file: (fileResult.name || 'unknown') as string,\r\n status: mapVitestStatus(assertion.status as string),\r\n duration: (assertion.duration as number) || 0,\r\n error: (assertion.failureMessages as string[] | undefined)?.length\r\n ? { message: (assertion.failureMessages as string[])[0] }\r\n : assertion.failureMessage\r\n ? { message: assertion.failureMessage as string }\r\n : undefined,\r\n retries: 0,\r\n });\r\n }\r\n\r\n // 如果没有 assertionResults 但有汇总数据,构造简要结果\r\n if (tests.length === 0 && fileResult.numPassingTests !== undefined) {\r\n tests.push(...buildTestsFromSummary(fileResult));\r\n }\r\n\r\n return tests;\r\n}\r\n\r\n/**\r\n * 从汇总数据构造测试用例列表\r\n */\r\nfunction buildTestsFromSummary(data: Record<string, unknown>): TestCaseResult[] {\r\n const tests: TestCaseResult[] = [];\r\n const counts: [number, TestStatus][] = [\r\n [(data.numPassedTests as number) || 0, 'passed'],\r\n [(data.numFailedTests as number) || 0, 'failed'],\r\n [(data.numPendingTests as number) || 0, 'skipped'],\r\n ];\r\n for (const [n, s] of counts) {\r\n for (let i = 0; i < n; i++) {\r\n tests.push({\r\n name: `${s} test ${i + 1}`,\r\n file: (data.name as string) || 'unknown',\r\n status: s,\r\n duration: 0,\r\n retries: 0,\r\n });\r\n }\r\n }\r\n return tests;\r\n}\r\n\r\n/**\r\n * 从 stdout 中尝试提取并解析 JSON(回退方案)\r\n */\r\nfunction parseFromStdout(output: string): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n coverage?: CoverageResult;\r\n} {\r\n // 尝试从输出中提取完整 JSON\r\n const jsonMatch = output.match(/\\{[\\s\\S]*\"testResults\"[\\s\\S]*\\}/);\r\n if (jsonMatch) {\r\n const data = JSON.parse(jsonMatch[0]);\r\n const suites: TestSuiteResult[] = [];\r\n\r\n if (data.testResults && Array.isArray(data.testResults)) {\r\n for (const fileResult of data.testResults) {\r\n suites.push({\r\n name: path.basename(fileResult.name || 'unknown'),\r\n file: fileResult.name || 'unknown',\r\n type: 'unit' as TestType,\r\n status: mapVitestStatus(fileResult.status),\r\n duration: fileResult.duration || 0,\r\n tests: parseTestResults(fileResult),\r\n });\r\n }\r\n }\r\n\r\n const success = data.success !== false && suites.every((s) => s.status !== 'failed');\r\n let coverage: CoverageResult | undefined;\r\n if (data.coverageMap) coverage = extractCoverage(data.coverageMap);\r\n\r\n return { success, suites, coverage };\r\n }\r\n\r\n // 尝试匹配其他 JSON 格式\r\n const anyJsonMatch = output.match(/\\{[\\s\\S]*\"numTotalTests\"[\\s\\S]*\\}/);\r\n if (anyJsonMatch) {\r\n return parseVitestJSON(anyJsonMatch[0]);\r\n }\r\n\r\n throw new Error('stdout 中未找到有效 JSON');\r\n}\r\n\r\n/**\r\n * 解析 vitest 文本输出(所有 JSON 解析失败时的最终回退方案)\r\n */\r\nfunction parseVitestTextOutput(output: string, hasError: boolean): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n} {\r\n const suites: TestSuiteResult[] = [];\r\n\r\n debug('vitest-text', '文本输出前 1000 字符:', output.substring(0, 1000));\r\n\r\n // 匹配 vitest 文本输出格式\r\n // 新版: ✓ tests/unit/example.test.ts (2)\r\n // 旧版: ✓ tests/unit/example.test.ts (2 tests)\r\n // 中文: ✓ tests/unit/example.test.ts (2 个测试)\r\n const suiteRegex = /[✓✗×✕]\\s+(.+\\.test\\.(ts|js)|.+\\.spec\\.(ts|js))\\s*\\((\\d+)[^)]*\\)/i;\r\n const lines = output.split('\\n');\r\n\r\n let totalPassed = 0;\r\n let totalFailed = 0;\r\n\r\n for (const line of lines) {\r\n const match = line.match(suiteRegex);\r\n if (match) {\r\n const file = match[1];\r\n const testCount = parseInt(match[4], 10);\r\n const isPassed = line.includes('✓');\r\n\r\n if (isPassed) totalPassed += testCount;\r\n else totalFailed += testCount;\r\n\r\n suites.push({\r\n name: path.basename(file),\r\n file,\r\n type: 'unit',\r\n status: isPassed ? 'passed' : 'failed',\r\n duration: 0,\r\n tests: Array.from({ length: testCount }, (_, i) => ({\r\n name: `test ${i + 1}`,\r\n file,\r\n status: isPassed ? ('passed' as TestStatus) : ('failed' as TestStatus),\r\n duration: 0,\r\n retries: 0,\r\n })),\r\n });\r\n }\r\n }\r\n\r\n // 也尝试匹配总结行\r\n // Test Files 2 passed (2)\r\n // Tests 5 passed (5)\r\n if (suites.length === 0) {\r\n const summaryMatch = output.match(/Tests\\s+(\\d+)\\s+(passed|failed)/i);\r\n if (summaryMatch) {\r\n const count = parseInt(summaryMatch[1], 10);\r\n const status = summaryMatch[2].toLowerCase() === 'passed' ? 'passed' : 'failed';\r\n suites.push({\r\n name: 'Vitest Summary',\r\n file: 'unknown',\r\n type: 'unit',\r\n status,\r\n duration: 0,\r\n tests: Array.from({ length: count }, (_, i) => ({\r\n name: `test ${i + 1}`,\r\n file: 'unknown',\r\n status: status as TestStatus,\r\n duration: 0,\r\n retries: 0,\r\n })),\r\n });\r\n }\r\n }\r\n\r\n debug('vitest-text', `解析到 ${suites.length} 个套件, ${totalPassed} 通过, ${totalFailed} 失败`);\r\n\r\n return {\r\n success: !hasError || totalFailed === 0,\r\n suites,\r\n };\r\n}\r\n\r\n/**\r\n * 映射 vitest 状态到 QAT 状态\r\n */\r\nfunction mapVitestStatus(status: string | undefined): TestStatus {\r\n if (!status) return 'pending';\r\n switch (status) {\r\n case 'passed':\r\n case 'pass':\r\n case 'done':\r\n return 'passed';\r\n case 'failed':\r\n case 'fail':\r\n return 'failed';\r\n case 'skipped':\r\n case 'skip':\r\n case 'pending':\r\n case 'todo':\r\n return 'skipped';\r\n default:\r\n return 'pending';\r\n }\r\n}\r\n\r\n/**\r\n * 从 vitest coverage map 提取汇总数据\r\n */\r\nfunction extractCoverage(coverageMap: Record<string, unknown>): CoverageResult {\r\n let totalStatements = 0;\r\n let coveredStatements = 0;\r\n let totalFunctions = 0;\r\n let coveredFunctions = 0;\r\n let totalBranches = 0;\r\n let coveredBranches = 0;\r\n let totalLines = 0;\r\n let coveredLines = 0;\r\n\r\n for (const fileCov of Object.values(coverageMap) as Record<string, unknown>[]) {\r\n const s = fileCov.s as Record<string, number> || {};\r\n const f = fileCov.f as Record<string, number> || {};\r\n const b = fileCov.b as Record<string, Record<string, number>> || {};\r\n\r\n for (const count of Object.values(s)) {\r\n totalLines++;\r\n if (count > 0) coveredLines++;\r\n }\r\n\r\n for (const count of Object.values(f)) {\r\n totalFunctions++;\r\n if (count > 0) coveredFunctions++;\r\n }\r\n\r\n for (const branch of Object.values(b)) {\r\n for (const count of Object.values(branch)) {\r\n totalBranches++;\r\n if (count > 0) coveredBranches++;\r\n }\r\n }\r\n }\r\n\r\n totalStatements = totalLines;\r\n coveredStatements = coveredLines;\r\n\r\n return {\r\n lines: totalLines > 0 ? coveredLines / totalLines : 0,\r\n statements: totalStatements > 0 ? coveredStatements / totalStatements : 0,\r\n functions: totalFunctions > 0 ? coveredFunctions / totalFunctions : 0,\r\n branches: totalBranches > 0 ? coveredBranches / totalBranches : 0,\r\n };\r\n}\r\n","/**\r\n * Playwright 运行器 - 通过子进程调用Playwright,解析输出收集结果\r\n */\r\n\r\nimport { execFile } from 'node:child_process';\r\nimport path from 'node:path';\r\nimport type { TestRunResult, TestSuiteResult, TestCaseResult, TestStatus, TestType } from '../types/index.js';\r\n\r\nexport interface PlaywrightRunnerOptions {\r\n type: TestType;\r\n files?: string[];\r\n browsers?: ('chromium' | 'firefox' | 'webkit')[];\r\n headed?: boolean;\r\n configPath?: string;\r\n /** 额外传递给 playwright 的参数 */\r\n extraArgs?: string[];\r\n}\r\n\r\n/**\r\n * 执行Playwright测试\r\n */\r\nexport async function runPlaywright(options: PlaywrightRunnerOptions): Promise<TestRunResult> {\r\n const startTime = Date.now();\r\n\r\n const args = buildPlaywrightArgs(options);\r\n\r\n try {\r\n const result = await execPlaywright(args);\r\n const endTime = Date.now();\r\n\r\n return {\r\n type: options.type,\r\n status: result.success ? 'passed' : 'failed',\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites: result.suites,\r\n };\r\n } catch (error) {\r\n const endTime = Date.now();\r\n return {\r\n type: options.type,\r\n status: 'failed',\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites: [{\r\n name: 'Playwright Runner',\r\n file: options.files?.[0] || 'unknown',\r\n type: options.type,\r\n status: 'failed',\r\n duration: endTime - startTime,\r\n tests: [{\r\n name: 'Runner Error',\r\n file: options.files?.[0] || 'unknown',\r\n status: 'failed',\r\n duration: 0,\r\n error: {\r\n message: error instanceof Error ? error.message : String(error),\r\n },\r\n retries: 0,\r\n }],\r\n }],\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * 构建playwright命令参数\r\n */\r\nfunction buildPlaywrightArgs(options: PlaywrightRunnerOptions): string[] {\r\n const args = ['playwright', 'test', '--reporter=json'];\r\n\r\n // 指定测试文件或目录\r\n if (options.files && options.files.length > 0) {\r\n args.push(...options.files);\r\n } else {\r\n // 根据测试类型确定测试目录\r\n const dirMap: Record<string, string> = {\r\n e2e: 'tests/e2e',\r\n visual: 'tests/visual',\r\n performance: 'tests/e2e',\r\n };\r\n const testDir = dirMap[options.type];\r\n if (testDir) {\r\n args.push(testDir);\r\n }\r\n }\r\n\r\n // 指定浏览器\r\n if (options.browsers && options.browsers.length > 0) {\r\n for (const browser of options.browsers) {\r\n args.push('--project', browser);\r\n }\r\n }\r\n\r\n // 有头模式\r\n if (options.headed) {\r\n args.push('--headed');\r\n }\r\n\r\n // 配置文件\r\n if (options.configPath) {\r\n args.push('--config', options.configPath);\r\n }\r\n\r\n // 额外参数\r\n if (options.extraArgs) {\r\n args.push(...options.extraArgs);\r\n }\r\n\r\n return args;\r\n}\r\n\r\n/**\r\n * 执行playwright命令并解析JSON输出\r\n */\r\nasync function execPlaywright(args: string[]): Promise<{\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n}> {\r\n return new Promise((resolve, reject) => {\r\n const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n const child = execFile(npx, args, {\r\n cwd: process.cwd(),\r\n env: { ...process.env, FORCE_COLOR: '0', PLAYWRIGHT_JSON_OUTPUT_DIR: '' },\r\n maxBuffer: 50 * 1024 * 1024,\r\n shell: true,\r\n }, (error, stdout, stderr) => {\r\n const output = stdout || stderr || '';\r\n\r\n try {\r\n const parsed = parsePlaywrightJSONOutput(output);\r\n resolve(parsed);\r\n } catch {\r\n if (output) {\r\n resolve(parsePlaywrightTextOutput(output, !!error));\r\n } else if (error && error.message.includes('ENOENT')) {\r\n reject(new Error('未找到 playwright,请确保已安装: npm install -D @playwright/test'));\r\n } else {\r\n resolve({ success: !error, suites: [] });\r\n }\r\n }\r\n });\r\n\r\n child.on('error', (err) => {\r\n reject(new Error(`Playwright 执行失败: ${err.message}`));\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 解析 Playwright JSON 输出\r\n */\r\nfunction parsePlaywrightJSONOutput(output: string): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n} {\r\n // Playwright JSON reporter 输出格式\r\n // 尝试提取 JSON\r\n const jsonMatch = output.match(/\\{[\\s\\S]*\"suites\"[\\s\\S]*\\}/);\r\n\r\n if (!jsonMatch) {\r\n return parsePlaywrightTextOutput(output, false);\r\n }\r\n\r\n try {\r\n const data = JSON.parse(jsonMatch[0]);\r\n const suites: TestSuiteResult[] = [];\r\n\r\n // Playwright JSON reporter 格式\r\n if (data.suites && Array.isArray(data.suites)) {\r\n for (const suiteData of data.suites) {\r\n extractPlaywrightSuites(suiteData, suites);\r\n }\r\n }\r\n\r\n const success = data.stats?.status === 'passed' || suites.every((s) => s.status !== 'failed');\r\n\r\n return { success, suites };\r\n } catch {\r\n return parsePlaywrightTextOutput(output, false);\r\n }\r\n}\r\n\r\n/**\r\n * 递归提取 Playwright 套件\r\n */\r\nfunction extractPlaywrightSuites(\r\n suiteData: Record<string, unknown>,\r\n result: TestSuiteResult[],\r\n parentPath = '',\r\n): void {\r\n const suiteName = (suiteData.title as string) || 'unknown';\r\n const suitePath = parentPath ? `${parentPath} > ${suiteName}` : suiteName;\r\n\r\n // 如果有 specs,这是一个叶子套件\r\n if (suiteData.specs && Array.isArray(suiteData.specs)) {\r\n const tests: TestCaseResult[] = (suiteData.specs as Record<string, unknown>[]).map((spec) => {\r\n const specTitle = (spec.title as string) || 'unknown';\r\n const specFile = (spec.file as string) || 'unknown';\r\n // 从 tests 数组获取状态\r\n const specTests = spec.tests as Record<string, unknown>[] | undefined;\r\n let status: TestStatus = 'pending';\r\n let duration = 0;\r\n let error: { message: string; stack?: string } | undefined;\r\n\r\n if (specTests && specTests.length > 0) {\r\n const lastRun = specTests[specTests.length - 1] as Record<string, unknown>;\r\n const results = lastRun.results as Record<string, unknown>[] | undefined;\r\n if (results && results.length > 0) {\r\n const lastResult = results[results.length - 1] as Record<string, unknown>;\r\n status = mapPlaywrightStatus(lastResult.status as string);\r\n duration = (lastResult.duration as number) || 0;\r\n if (lastResult.error) {\r\n const err = lastResult.error as Record<string, unknown>;\r\n error = {\r\n message: (err.message as string) || '',\r\n stack: err.stack as string | undefined,\r\n };\r\n }\r\n }\r\n }\r\n\r\n return {\r\n name: specTitle,\r\n file: specFile,\r\n status,\r\n duration,\r\n error,\r\n retries: 0,\r\n };\r\n });\r\n\r\n const suiteStatus = tests.some((t) => t.status === 'failed')\r\n ? 'failed'\r\n : tests.every((t) => t.status === 'skipped')\r\n ? 'skipped'\r\n : 'passed';\r\n\r\n result.push({\r\n name: suiteName,\r\n file: ((suiteData.specs as Record<string, unknown>[])[0]?.file as string) || 'unknown',\r\n type: 'e2e',\r\n status: suiteStatus,\r\n duration: tests.reduce((sum, t) => sum + t.duration, 0),\r\n tests,\r\n });\r\n }\r\n\r\n // 递归处理子套件\r\n if (suiteData.suites && Array.isArray(suiteData.suites)) {\r\n for (const child of suiteData.suites as Record<string, unknown>[]) {\r\n extractPlaywrightSuites(child, result, suitePath);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 解析 Playwright 文本输出\r\n */\r\nfunction parsePlaywrightTextOutput(output: string, hasError: boolean): {\r\n success: boolean;\r\n suites: TestSuiteResult[];\r\n} {\r\n const suites: TestSuiteResult[] = [];\r\n let totalPassed = 0;\r\n let totalFailed = 0;\r\n let totalSkipped = 0;\r\n\r\n // 匹配 Playwright 文本输出\r\n // ✓ 1 test.ts:3:1 › login page › should display login form (2s)\r\n // ✘ 2 test.ts:10:1 › login page › should submit form (1s)\r\n // - 3 test.ts:20:1 › login page › should show error (skipped)\r\n const testRegex = /^\\s*([✓✘×\\-])\\s+\\d+\\s+(.+\\.test\\.ts|.+\\.spec\\.ts)\\s*:\\d+:\\d+\\s*›\\s*(.+?)\\s*\\((\\d+)ms?\\)/;\r\n\r\n for (const line of output.split('\\n')) {\r\n const match = line.match(testRegex);\r\n if (match) {\r\n const symbol = match[1];\r\n const file = match[2];\r\n const testName = match[3];\r\n const duration = parseInt(match[4], 10);\r\n\r\n let status: TestStatus;\r\n if (symbol === '✓') {\r\n status = 'passed';\r\n totalPassed++;\r\n } else if (symbol === '✘' || symbol === '×') {\r\n status = 'failed';\r\n totalFailed++;\r\n } else {\r\n status = 'skipped';\r\n totalSkipped++;\r\n }\r\n\r\n // 按文件分组\r\n let existingSuite = suites.find((s) => s.file === file);\r\n if (!existingSuite) {\r\n existingSuite = {\r\n name: path.basename(file),\r\n file,\r\n type: 'e2e',\r\n status: 'passed',\r\n duration: 0,\r\n tests: [],\r\n };\r\n suites.push(existingSuite);\r\n }\r\n\r\n existingSuite.tests.push({\r\n name: testName,\r\n file,\r\n status,\r\n duration,\r\n retries: 0,\r\n });\r\n\r\n if (status === 'failed') {\r\n existingSuite.status = 'failed';\r\n }\r\n }\r\n }\r\n\r\n // 更新套件状态和持续时间\r\n for (const suite of suites) {\r\n if (suite.tests.some((t) => t.status === 'failed')) {\r\n suite.status = 'failed';\r\n }\r\n suite.duration = suite.tests.reduce((sum, t) => sum + t.duration, 0);\r\n }\r\n\r\n // 匹配最终汇总\r\n // 3 passed, 1 failed, 1 skipped\r\n const summaryRegex = /(\\d+)\\s+passed.*?(\\d+)\\s+failed.*?(\\d+)\\s+skipped/i;\r\n const summaryMatch = output.match(summaryRegex);\r\n if (summaryMatch && suites.length === 0) {\r\n // 从汇总推断\r\n totalPassed = parseInt(summaryMatch[1], 10);\r\n totalFailed = parseInt(summaryMatch[2], 10);\r\n totalSkipped = parseInt(summaryMatch[3], 10);\r\n }\r\n\r\n return {\r\n success: !hasError || totalFailed === 0,\r\n suites,\r\n };\r\n}\r\n\r\n/**\r\n * 映射 Playwright 状态\r\n */\r\nfunction mapPlaywrightStatus(status: string): TestStatus {\r\n switch (status) {\r\n case 'passed':\r\n case 'pass':\r\n return 'passed';\r\n case 'failed':\r\n case 'fail':\r\n case 'timedOut':\r\n case 'interrupted':\r\n return 'failed';\r\n case 'skipped':\r\n case 'skip':\r\n return 'skipped';\r\n default:\r\n return 'pending';\r\n }\r\n}\r\n","/**\r\n * Lighthouse 运行器 - 通过子进程调用Lighthouse,提取性能指标\r\n */\r\n\r\nimport { execFile } from 'node:child_process';\r\nimport type { TestRunResult, TestSuiteResult, TestCaseResult, TestStatus, PerformanceMetrics } from '../types/index.js';\r\n\r\nexport interface LighthouseRunnerOptions {\r\n urls: string[];\r\n runs?: number;\r\n thresholds?: Partial<Record<'performance' | 'accessibility' | 'best-practices' | 'seo', number>>;\r\n /** 额外传递给 lighthouse 的参数 */\r\n extraArgs?: string[];\r\n}\r\n\r\n/**\r\n * 执行Lighthouse性能测试\r\n */\r\nexport async function runLighthouse(options: LighthouseRunnerOptions): Promise<TestRunResult> {\r\n const startTime = Date.now();\r\n const runs = options.runs || 3;\r\n const urls = options.urls;\r\n\r\n if (urls.length === 0) {\r\n return {\r\n type: 'performance',\r\n status: 'skipped',\r\n startTime,\r\n endTime: Date.now(),\r\n duration: 0,\r\n suites: [],\r\n };\r\n }\r\n\r\n const allResults: LighthouseRunResult[] = [];\r\n\r\n for (const url of urls) {\r\n try {\r\n const urlResults: LighthouseRunResult[] = [];\r\n\r\n for (let i = 0; i < runs; i++) {\r\n const result = await runLighthouseOnce(url, options.extraArgs);\r\n urlResults.push(result);\r\n }\r\n\r\n // 取中位数\r\n const median = getMedianResult(urlResults);\r\n allResults.push(median);\r\n } catch (error) {\r\n allResults.push({\r\n url,\r\n error: error instanceof Error ? error.message : String(error),\r\n scores: { performance: 0, accessibility: 0, bestPractices: 0, seo: 0 },\r\n metrics: {},\r\n });\r\n }\r\n }\r\n\r\n const endTime = Date.now();\r\n\r\n // 构建测试结果\r\n const suites: TestSuiteResult[] = allResults.map((result) => {\r\n const tests: TestCaseResult[] = [\r\n makeTestResult('Performance Score', result.scores.performance, options.thresholds?.performance),\r\n makeTestResult('Accessibility Score', result.scores.accessibility, options.thresholds?.accessibility),\r\n makeTestResult('Best Practices Score', result.scores.bestPractices, options.thresholds?.['best-practices']),\r\n makeTestResult('SEO Score', result.scores.seo, options.thresholds?.seo),\r\n ];\r\n\r\n // 添加核心 Web 指标测试\r\n if (result.metrics.lcp !== undefined) {\r\n tests.push(makeTestResult('Largest Contentful Paint', result.metrics.lcp, 2500, true));\r\n }\r\n if (result.metrics.fid !== undefined) {\r\n tests.push(makeTestResult('First Input Delay', result.metrics.fid, 100, true));\r\n }\r\n if (result.metrics.cls !== undefined) {\r\n tests.push(makeTestResult('Cumulative Layout Shift', result.metrics.cls, 0.1, true));\r\n }\r\n\r\n const suiteStatus: TestStatus = result.error\r\n ? 'failed'\r\n : tests.some((t) => t.status === 'failed')\r\n ? 'failed'\r\n : 'passed';\r\n\r\n return {\r\n name: `Performance: ${result.url}`,\r\n file: result.url,\r\n type: 'performance',\r\n status: suiteStatus,\r\n duration: endTime - startTime,\r\n tests,\r\n };\r\n });\r\n\r\n // 计算平均指标\r\n const avgMetrics = calculateAverageMetrics(allResults);\r\n\r\n const overallStatus = suites.some((s) => s.status === 'failed') ? 'failed' : 'passed';\r\n\r\n return {\r\n type: 'performance',\r\n status: overallStatus,\r\n startTime,\r\n endTime,\r\n duration: endTime - startTime,\r\n suites,\r\n performance: avgMetrics,\r\n };\r\n}\r\n\r\ninterface LighthouseRunResult {\r\n url: string;\r\n error?: string;\r\n scores: {\r\n performance: number;\r\n accessibility: number;\r\n bestPractices: number;\r\n seo: number;\r\n };\r\n metrics: {\r\n lcp?: number;\r\n fid?: number;\r\n cls?: number;\r\n };\r\n}\r\n\r\n/**\r\n * 单次 Lighthouse 运行\r\n */\r\nasync function runLighthouseOnce(url: string, extraArgs?: string[]): Promise<LighthouseRunResult> {\r\n const args = [\r\n 'lighthouse', url,\r\n '--output=json',\r\n '--quiet',\r\n '--chrome-flags=--headless --no-sandbox',\r\n '--only-categories=performance,accessibility,best-practices,seo',\r\n ];\r\n\r\n if (extraArgs) {\r\n args.push(...extraArgs);\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n execFile(npx, args, {\r\n cwd: process.cwd(),\r\n maxBuffer: 50 * 1024 * 1024,\r\n shell: true,\r\n }, (error, stdout) => {\r\n if (error && !stdout) {\r\n reject(new Error(`Lighthouse 执行失败: ${error.message}`));\r\n return;\r\n }\r\n\r\n try {\r\n const data = JSON.parse(stdout);\r\n const categories = data.categories || {};\r\n const audits = data.audits || {};\r\n\r\n resolve({\r\n url,\r\n scores: {\r\n performance: Math.round((categories.performance?.score || 0) * 100),\r\n accessibility: Math.round((categories.accessibility?.score || 0) * 100),\r\n bestPractices: Math.round((categories['best-practices']?.score || 0) * 100),\r\n seo: Math.round((categories.seo?.score || 0) * 100),\r\n },\r\n metrics: {\r\n lcp: audits['largest-contentful-paint']?.numericValue,\r\n fid: audits['max-potential-fid']?.numericValue,\r\n cls: audits['cumulative-layout-shift']?.numericValue,\r\n },\r\n });\r\n } catch {\r\n reject(new Error('Lighthouse 输出解析失败'));\r\n }\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 获取中位数结果\r\n */\r\nfunction getMedianResult(results: LighthouseRunResult[]): LighthouseRunResult {\r\n if (results.length === 1) return results[0];\r\n\r\n // 按 performance score 排序取中位数\r\n const sorted = [...results].sort(\r\n (a, b) => a.scores.performance - b.scores.performance,\r\n );\r\n const mid = Math.floor(sorted.length / 2);\r\n return sorted[mid];\r\n}\r\n\r\n/**\r\n * 创建测试结果\r\n */\r\nfunction makeTestResult(\r\n name: string,\r\n value: number,\r\n threshold?: number,\r\n isLowerBetter = false,\r\n): TestCaseResult {\r\n let status: TestStatus = 'passed';\r\n let error: { message: string } | undefined;\r\n\r\n if (threshold !== undefined) {\r\n const passed = isLowerBetter ? value <= threshold : value >= threshold;\r\n if (!passed) {\r\n status = 'failed';\r\n error = {\r\n message: `${name}: ${isLowerBetter ? `${value}ms` : value} (${isLowerBetter ? '>' : '<'} threshold ${isLowerBetter ? `${threshold}ms` : threshold})`,\r\n };\r\n }\r\n }\r\n\r\n return {\r\n name,\r\n file: 'lighthouse',\r\n status,\r\n duration: 0,\r\n error,\r\n retries: 0,\r\n };\r\n}\r\n\r\n/**\r\n * 计算平均分数\r\n */\r\nfunction calculateAverageScores(results: LighthouseRunResult[]): PerformanceMetrics {\r\n const valid = results.filter((r) => !r.error);\r\n if (valid.length === 0) {\r\n return { performance: 0, accessibility: 0, bestPractices: 0, seo: 0 };\r\n }\r\n\r\n return {\r\n performance: Math.round(valid.reduce((s, r) => s + r.scores.performance, 0) / valid.length),\r\n accessibility: Math.round(valid.reduce((s, r) => s + r.scores.accessibility, 0) / valid.length),\r\n bestPractices: Math.round(valid.reduce((s, r) => s + r.scores.bestPractices, 0) / valid.length),\r\n seo: Math.round(valid.reduce((s, r) => s + r.scores.seo, 0) / valid.length),\r\n };\r\n}\r\n\r\n/**\r\n * 计算平均指标\r\n */\r\nfunction calculateAverageMetrics(results: LighthouseRunResult[]): PerformanceMetrics {\r\n const valid = results.filter((r) => !r.error);\r\n const scores = calculateAverageScores(results);\r\n\r\n const lcpValues = valid.map((r) => r.metrics.lcp).filter((v): v is number => v !== undefined);\r\n const fidValues = valid.map((r) => r.metrics.fid).filter((v): v is number => v !== undefined);\r\n const clsValues = valid.map((r) => r.metrics.cls).filter((v): v is number => v !== undefined);\r\n\r\n return {\r\n ...scores,\r\n lcp: lcpValues.length > 0 ? lcpValues.reduce((s, v) => s + v, 0) / lcpValues.length : undefined,\r\n fid: fidValues.length > 0 ? fidValues.reduce((s, v) => s + v, 0) / fidValues.length : undefined,\r\n cls: clsValues.length > 0 ? clsValues.reduce((s, v) => s + v, 0) / clsValues.length : undefined,\r\n };\r\n}\r\n","/**\r\n * 默认空实现 - 未配置AI时的占位Provider\r\n */\r\n\r\nimport type { AIProvider, AICapability, AIGenerateTestRequest, AIGenerateTestResponse, AIAnalyzeResultRequest, AIAnalyzeResultResponse, AIReviewTestRequest, AIReviewTestResponse } from '../types/ai.js';\r\nimport type { TestError } from '../types/index.js';\r\n\r\nconst NOOP_CAPABILITIES: AICapability = {\r\n generateTest: false,\r\n analyzeResult: false,\r\n suggestFix: false,\r\n};\r\n\r\nexport class NoopAIProvider implements AIProvider {\r\n readonly name = 'noop';\r\n readonly capabilities = NOOP_CAPABILITIES;\r\n\r\n async generateTest(_req: AIGenerateTestRequest): Promise<AIGenerateTestResponse> {\r\n throw new Error(\r\n 'AI功能未配置。请在 qat.config.ts 中配置 ai.provider 后使用此功能。',\r\n );\r\n }\r\n\r\n async analyzeResult(_req: AIAnalyzeResultRequest): Promise<AIAnalyzeResultResponse> {\r\n throw new Error(\r\n 'AI功能未配置。请在 qat.config.ts 中配置 ai.provider 后使用此功能。',\r\n );\r\n }\r\n\r\n async suggestFix(_error: TestError): Promise<string[]> {\r\n throw new Error(\r\n 'AI功能未配置。请在 qat.config.ts 中配置 ai.provider 后使用此功能。',\r\n );\r\n }\r\n\r\n async reviewTest(_req: AIReviewTestRequest): Promise<AIReviewTestResponse> {\r\n throw new Error(\r\n 'AI功能未配置。请在 qat.config.ts 中配置 ai.provider 后使用此功能。',\r\n );\r\n }\r\n}\r\n","/**\r\n * OpenAI 兼容 Provider - 支持 OpenAI / DeepSeek / Moonshot / Ollama 等\r\n */\r\n\r\nimport type {\r\n AIProvider,\r\n AICapability,\r\n AIGenerateTestRequest,\r\n AIGenerateTestResponse,\r\n AIAnalyzeResultRequest,\r\n AIAnalyzeResultResponse,\r\n AIReviewTestRequest,\r\n AIReviewTestResponse,\r\n} from '../types/ai.js';\r\nimport type { TestError } from '../types/index.js';\r\n\r\n/** OpenAI Chat Completion 响应格式 */\r\ninterface ChatCompletionResponse {\r\n choices: Array<{\r\n message: {\r\n content: string;\r\n };\r\n finish_reason: string;\r\n }>;\r\n usage?: {\r\n prompt_tokens: number;\r\n completion_tokens: number;\r\n total_tokens: number;\r\n };\r\n}\r\n\r\nexport class OpenAICompatibleProvider implements AIProvider {\r\n readonly name: string;\r\n readonly capabilities: AICapability = {\r\n generateTest: true,\r\n analyzeResult: true,\r\n suggestFix: true,\r\n };\r\n\r\n private apiKey: string;\r\n private model: string;\r\n private baseUrl: string;\r\n\r\n constructor(config: { provider: string; apiKey?: string; model?: string; baseUrl?: string }) {\r\n this.name = config.provider;\r\n this.apiKey = config.apiKey || '';\r\n this.model = config.model || this.getDefaultModel(config.provider);\r\n this.baseUrl = config.baseUrl || this.getDefaultBaseUrl(config.provider);\r\n }\r\n\r\n async generateTest(req: AIGenerateTestRequest): Promise<AIGenerateTestResponse> {\r\n const systemPrompt = this.buildGenerateTestSystemPrompt(req);\r\n const userPrompt = this.buildGenerateTestUserPrompt(req);\r\n\r\n const content = await this.chat(systemPrompt, userPrompt);\r\n return this.parseGenerateTestResponse(content);\r\n }\r\n\r\n async analyzeResult(req: AIAnalyzeResultRequest): Promise<AIAnalyzeResultResponse> {\r\n const systemPrompt = `你是一个专业的测试分析专家。分析测试运行结果,找出问题根因,给出具体可操作的改进建议。\r\n输出格式:\r\n1. 分析摘要(1-3句话)\r\n2. 改进建议列表(每条建议具体、可操作)`;\r\n\r\n const resultSummary = req.testResults.map((r) => {\r\n const failed = r.suites.flatMap((s) => s.tests.filter((t) => t.status === 'failed'));\r\n return `类型: ${r.type}, 状态: ${r.status}, 耗时: ${r.duration}ms, 失败用例: ${failed.length}`;\r\n }).join('\\n');\r\n\r\n const errorDetails = req.errorLogs?.join('\\n') || req.testResults\r\n .flatMap((r) => r.suites.flatMap((s) => s.tests.filter((t) => t.status === 'failed' && t.error)))\r\n .map((t) => `[${t.name}] ${t.error?.message}`)\r\n .join('\\n') || '无错误详情';\r\n\r\n const userPrompt = `测试结果:\\n${resultSummary}\\n\\n错误详情:\\n${errorDetails}`;\r\n\r\n const content = await this.chat(systemPrompt, userPrompt);\r\n\r\n return {\r\n analysis: content.split('\\n')[0] || content.slice(0, 200),\r\n suggestions: content.split('\\n').filter((l) => l.trim().startsWith('-') || l.trim().startsWith('•') || l.trim().match(/^\\d+\\./)).map((l) => l.replace(/^[-•\\d.]+\\s*/, '').trim()).filter(Boolean),\r\n severity: req.testResults.some((r) => r.status === 'failed') ? 'error' : 'info',\r\n };\r\n }\r\n\r\n async suggestFix(error: TestError): Promise<string[]> {\r\n const systemPrompt = `你是一个专业的代码修复专家。根据测试错误信息,给出具体的修复建议。\r\n每条建议应该包含:\r\n1. 问题定位\r\n2. 修复方案\r\n3. 示例代码(如果适用)`;\r\n\r\n const userPrompt = `错误信息: ${error.message}\r\n${error.stack ? `堆栈: ${error.stack}` : ''}\r\n${error.expected ? `期望值: ${error.expected}` : ''}\r\n${error.actual ? `实际值: ${error.actual}` : ''}`;\r\n\r\n const content = await this.chat(systemPrompt, userPrompt);\r\n\r\n return content\r\n .split('\\n')\r\n .filter((l) => l.trim().startsWith('-') || l.trim().startsWith('•') || l.trim().match(/^\\d+\\./))\r\n .map((l) => l.replace(/^[-•\\d.]+\\s*/, '').trim())\r\n .filter(Boolean);\r\n }\r\n\r\n async reviewTest(req: AIReviewTestRequest): Promise<AIReviewTestResponse> {\r\n const systemPrompt = this.buildReviewTestSystemPrompt(req);\r\n const userPrompt = this.buildReviewTestUserPrompt(req);\r\n\r\n const content = await this.chat(systemPrompt, userPrompt);\r\n return this.parseReviewTestResponse(content);\r\n }\r\n\r\n // ─── 内部方法 ──────────────────────────────────────────────\r\n\r\n /**\r\n * 测试连通性 - 发送一条简单请求验证 API 可达\r\n * @returns 连通结果\r\n */\r\n async testConnection(): Promise<{ ok: boolean; message: string; latencyMs?: number }> {\r\n const url = `${this.baseUrl}/chat/completions`;\r\n const start = Date.now();\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n\r\n if (this.apiKey) {\r\n headers['Authorization'] = `Bearer ${this.apiKey}`;\r\n }\r\n\r\n try {\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify({\r\n model: this.model,\r\n messages: [{ role: 'user', content: 'Hi' }],\r\n max_tokens: 5,\r\n }),\r\n signal: AbortSignal.timeout(15000), // 15s timeout\r\n });\r\n\r\n const latencyMs = Date.now() - start;\r\n\r\n if (response.ok) {\r\n const data = (await response.json()) as ChatCompletionResponse;\r\n if (data.choices?.[0]?.message?.content !== undefined) {\r\n return { ok: true, message: `连通正常 (${latencyMs}ms)`, latencyMs };\r\n }\r\n return { ok: false, message: `API 返回格式异常: ${JSON.stringify(data).slice(0, 200)}`, latencyMs };\r\n }\r\n\r\n // 非 2xx\r\n const text = await response.text().catch(() => '');\r\n let detail = text.slice(0, 300);\r\n\r\n // 尝试提取错误信息\r\n try {\r\n const errObj = JSON.parse(text);\r\n if (errObj.error?.message) detail = errObj.error.message;\r\n } catch { /* ignore */ }\r\n\r\n if (response.status === 401) {\r\n return { ok: false, message: `认证失败: API Key 无效或已过期`, latencyMs };\r\n }\r\n if (response.status === 404) {\r\n return { ok: false, message: `接口不存在: 请检查 baseUrl 是否正确 (状态 404)`, latencyMs };\r\n }\r\n if (response.status === 429) {\r\n return { ok: false, message: `请求频率超限: 请稍后重试 (429)`, latencyMs };\r\n }\r\n\r\n return { ok: false, message: `HTTP ${response.status}: ${detail}`, latencyMs };\r\n } catch (error) {\r\n const latencyMs = Date.now() - start;\r\n if (error instanceof TypeError && error.message.includes('fetch')) {\r\n return { ok: false, message: `网络错误: 无法连接到 ${this.baseUrl},请检查地址是否正确`, latencyMs };\r\n }\r\n if (error instanceof Error && error.name === 'TimeoutError') {\r\n return { ok: false, message: `连接超时: ${this.baseUrl} 未在 15s 内响应`, latencyMs };\r\n }\r\n return { ok: false, message: `连接失败: ${error instanceof Error ? error.message : String(error)}`, latencyMs };\r\n }\r\n }\r\n\r\n private async chat(systemPrompt: string, userPrompt: string): Promise<string> {\r\n const url = `${this.baseUrl}/chat/completions`;\r\n\r\n const body = {\r\n model: this.model,\r\n messages: [\r\n { role: 'system', content: systemPrompt },\r\n { role: 'user', content: userPrompt },\r\n ],\r\n temperature: 0.3,\r\n max_tokens: 4096,\r\n };\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n\r\n // Ollama 不需要 Authorization header\r\n if (this.apiKey) {\r\n headers['Authorization'] = `Bearer ${this.apiKey}`;\r\n }\r\n\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body),\r\n signal: AbortSignal.timeout(60000), // 60s timeout\r\n });\r\n\r\n if (!response.ok) {\r\n const text = await response.text().catch(() => '');\r\n throw new Error(`AI API 请求失败 (${response.status}): ${text.slice(0, 500)}`);\r\n }\r\n\r\n const data = (await response.json()) as ChatCompletionResponse;\r\n\r\n if (!data.choices?.[0]?.message?.content) {\r\n throw new Error('AI API 返回空响应');\r\n }\r\n\r\n return data.choices[0].message.content;\r\n }\r\n\r\n private buildGenerateTestSystemPrompt(req: AIGenerateTestRequest): string {\r\n const typeMap: Record<string, string> = {\r\n unit: '单元测试(Vitest + @vue/test-utils)',\r\n component: '组件测试(Vitest + @vue/test-utils + mount)',\r\n e2e: 'E2E端到端测试(Playwright)',\r\n api: 'API接口测试(Vitest + fetch)',\r\n visual: '视觉回归测试(Playwright screenshot)',\r\n performance: '性能测试(Playwright + performance metrics)',\r\n };\r\n\r\n return `你是一个专业的前端测试工程师,擅长编写高质量的${typeMap[req.type] || req.type}。\r\n要求:\r\n1. 只输出测试代码,不要多余的解释\r\n2. 代码必须可直接运行,包含所有必要的 import\r\n3. 测试用例覆盖:正常路径、边界条件、错误处理\r\n4. 使用中文描述 it/test 块名称\r\n5. Vue 组件测试使用 @vue/test-utils 的 mount\r\n6. 如果有 props/emits 信息,务必针对每个 prop 和 emit 生成测试`;\r\n }\r\n\r\n private buildGenerateTestUserPrompt(req: AIGenerateTestRequest): string {\r\n let prompt = `请为以下文件生成${req.type}测试代码:\\n目标文件: ${req.target}\\n`;\r\n\r\n if (req.analysis) {\r\n prompt += '\\n源码分析结果:\\n';\r\n\r\n if (req.analysis.exports?.length > 0) {\r\n prompt += `导出项:\\n${req.analysis.exports.map((e) => {\r\n const params = e.params?.length ? `(${e.params.join(', ')})` : '';\r\n const asyncFlag = e.isAsync ? 'async ' : '';\r\n return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;\r\n }).join('\\n')}\\n`;\r\n }\r\n\r\n if (req.analysis.props?.length) {\r\n prompt += `Props:\\n${req.analysis.props.map((p) =>\r\n ` - ${p.name}: ${p.type}${p.required ? ' (必填)' : ' (可选)'}`,\r\n ).join('\\n')}\\n`;\r\n }\r\n\r\n if (req.analysis.emits?.length) {\r\n prompt += `Emits:\\n${req.analysis.emits.map((e) =>\r\n ` - ${e.name}${e.params?.length ? `(${e.params.join(', ')})` : ''}`,\r\n ).join('\\n')}\\n`;\r\n }\r\n\r\n if (req.analysis.methods?.length) {\r\n prompt += `Methods: ${req.analysis.methods.join(', ')}\\n`;\r\n }\r\n\r\n if (req.analysis.computed?.length) {\r\n prompt += `Computed: ${req.analysis.computed.join(', ')}\\n`;\r\n }\r\n }\r\n\r\n if (req.context) {\r\n prompt += `\\n源码内容:\\n\\`\\`\\`typescript\\n${req.context}\\n\\`\\`\\`\\n`;\r\n }\r\n\r\n if (req.framework) {\r\n prompt += `\\n框架: ${req.framework}`;\r\n }\r\n\r\n return prompt;\r\n }\r\n\r\n private parseGenerateTestResponse(content: string): AIGenerateTestResponse {\r\n // 提取代码块\r\n const codeBlockMatch = content.match(/```(?:typescript|ts|javascript|js)?\\s*\\n([\\s\\S]*?)```/);\r\n const code = codeBlockMatch\r\n ? codeBlockMatch[1].trim()\r\n : content.replace(/^(?:```[\\s\\S]*?\\n)?/, '').replace(/\\n?```$/, '').trim();\r\n\r\n // 提取描述(代码块之前的文字)\r\n const description = codeBlockMatch\r\n ? content.split('```')[0].trim().slice(0, 200)\r\n : 'AI generated test';\r\n\r\n return {\r\n code,\r\n description: description || 'AI generated test',\r\n confidence: 0.8,\r\n };\r\n }\r\n\r\n private buildReviewTestSystemPrompt(_req: AIReviewTestRequest): string {\r\n return `你是一位严谨的测试审计专家。你的职责是审查 AI 生成的测试用例是否与源码贴切且准确。\r\n\r\n审查标准:\r\n1. **测试类型匹配**:测试类型是否与源码文件性质匹配(如 .vue 应为组件测试,utils 应为单元测试)\r\n2. **覆盖完整性**:是否覆盖了源码中的核心导出项(函数、组件的 props/emits/methods)\r\n3. **断言有效性**:断言是否真实检验了被测行为,而非空断言或永真断言\r\n4. **导入正确性**:import 路径和模块是否正确\r\n5. **代码可运行性**:测试代码是否可直接运行,无语法错误\r\n\r\n输出格式(严格遵守):\r\nAPPROVED: true 或 false\r\nSCORE: 0.0 到 1.0 之间的评分\r\nFEEDBACK: 一句话审计意见\r\nISSUES: 问题列表(每行一个,格式 \"- 问题描述\")\r\nSUGGESTIONS: 改进建议列表(每行一个,格式 \"- 建议描述\")`;\r\n }\r\n\r\n private buildReviewTestUserPrompt(req: AIReviewTestRequest): string {\r\n let prompt = `请审查以下测试用例是否与源码贴切且准确。\r\n\r\n被测文件: ${req.target}\r\n测试类型: ${req.testType}\r\n\r\n--- 源码内容 ---\r\n\\`\\`\\`typescript\r\n${req.sourceCode}\r\n\\`\\`\\`\r\n\r\n--- 生成的测试代码 ---\r\n\\`\\`\\`typescript\r\n${req.testCode}\r\n\\`\\`\\``;\r\n\r\n if (req.analysis) {\r\n prompt += '\\n\\n--- 源码分析 ---';\r\n\r\n if (req.analysis.exports?.length > 0) {\r\n prompt += `\\n导出项:\\n${req.analysis.exports.map((e) => {\r\n const params = e.params?.length ? `(${e.params.join(', ')})` : '';\r\n const asyncFlag = e.isAsync ? 'async ' : '';\r\n return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;\r\n }).join('\\n')}`;\r\n }\r\n\r\n if (req.analysis.props?.length) {\r\n prompt += `\\nProps:\\n${req.analysis.props.map((p) =>\r\n ` - ${p.name}: ${p.type}${p.required ? ' (必填)' : ' (可选)'}`,\r\n ).join('\\n')}`;\r\n }\r\n\r\n if (req.analysis.emits?.length) {\r\n prompt += `\\nEmits:\\n${req.analysis.emits.map((e) =>\r\n ` - ${e.name}${e.params?.length ? `(${e.params.join(', ')})` : ''}`,\r\n ).join('\\n')}`;\r\n }\r\n }\r\n\r\n if (req.generationDescription) {\r\n prompt += `\\n\\n生成者说明: ${req.generationDescription}`;\r\n }\r\n\r\n return prompt;\r\n }\r\n\r\n private parseReviewTestResponse(content: string): AIReviewTestResponse {\r\n const approvedMatch = content.match(/APPROVED:\\s*(true|false)/i);\r\n const scoreMatch = content.match(/SCORE:\\s*([\\d.]+)/);\r\n const feedbackMatch = content.match(/FEEDBACK:\\s*(.+)/);\r\n\r\n const approved = approvedMatch ? approvedMatch[1].toLowerCase() === 'true' : false;\r\n const score = scoreMatch ? parseFloat(scoreMatch[1]) : (approved ? 0.7 : 0.3);\r\n const feedback = feedbackMatch ? feedbackMatch[1].trim() : '审计完成';\r\n\r\n // 提取问题列表\r\n const issues: string[] = [];\r\n const issuesMatch = content.match(/ISSUES:\\s*\\n([\\s\\S]*?)(?=SUGGESTIONS:|$)/);\r\n if (issuesMatch) {\r\n const lines = issuesMatch[1].split('\\n');\r\n for (const line of lines) {\r\n const trimmed = line.replace(/^[-•\\d.]+\\s*/, '').trim();\r\n if (trimmed) issues.push(trimmed);\r\n }\r\n }\r\n\r\n // 提取建议列表\r\n const suggestions: string[] = [];\r\n const suggestionsMatch = content.match(/SUGGESTIONS:\\s*\\n([\\s\\S]*?)$/);\r\n if (suggestionsMatch) {\r\n const lines = suggestionsMatch[1].split('\\n');\r\n for (const line of lines) {\r\n const trimmed = line.replace(/^[-•\\d.]+\\s*/, '').trim();\r\n if (trimmed) suggestions.push(trimmed);\r\n }\r\n }\r\n\r\n return {\r\n approved,\r\n score: Math.max(0, Math.min(1, score)),\r\n feedback,\r\n issues,\r\n suggestions,\r\n };\r\n }\r\n\r\n private getDefaultModel(provider: string): string {\r\n const modelMap: Record<string, string> = {\r\n openai: 'gpt-4o-mini',\r\n deepseek: 'deepseek-chat',\r\n moonshot: 'moonshot-v1-8k',\r\n zhipu: 'glm-4-flash',\r\n qwen: 'qwen-turbo',\r\n ollama: 'qwen2.5-coder:7b',\r\n };\r\n return modelMap[provider] || 'gpt-4o-mini';\r\n }\r\n\r\n private getDefaultBaseUrl(provider: string): string {\r\n const urlMap: Record<string, string> = {\r\n openai: 'https://api.openai.com/v1',\r\n deepseek: 'https://api.deepseek.com/v1',\r\n moonshot: 'https://api.moonshot.cn/v1',\r\n zhipu: 'https://open.bigmodel.cn/api/paas/v4',\r\n qwen: 'https://dashscope.aliyuncs.com/compatible-mode/v1',\r\n ollama: 'http://localhost:11434/v1',\r\n };\r\n return urlMap[provider] || 'https://api.openai.com/v1';\r\n }\r\n}\r\n","/**\r\n * AI Provider 管理中心 - 注册、加载、调度 AI Provider\r\n */\r\n\r\nimport type { AIConfig, AIPresetProvider } from '../types/ai.js';\r\nimport { NoopAIProvider } from './noop-provider.js';\r\nimport { OpenAICompatibleProvider } from './openai-provider.js';\r\n\r\n/** Provider 构造函数类型 */\r\nexport type AIProviderConstructor = new (config: AIConfig) => NoopAIProvider | OpenAICompatibleProvider;\r\n\r\n/** Provider 注册表 */\r\nconst providerRegistry = new Map<string, AIProviderConstructor>();\r\n\r\n/** 当前活跃的 Provider 实例 */\r\nlet activeProvider: NoopAIProvider | OpenAICompatibleProvider | null = null;\r\n\r\n// ─── 自动注册内置 Provider ──────────────────────────────────────\r\n\r\nconst BUILTIN_PROVIDERS: Array<{ id: string; ProviderClass: AIProviderConstructor }> = [\r\n { id: 'openai', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'deepseek', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'moonshot', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'zhipu', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'qwen', ProviderClass: OpenAICompatibleProvider },\r\n { id: 'ollama', ProviderClass: OpenAICompatibleProvider },\r\n];\r\n\r\nfor (const { id, ProviderClass } of BUILTIN_PROVIDERS) {\r\n providerRegistry.set(id, ProviderClass);\r\n}\r\n\r\n/** 预置 Provider 信息(用于 init 向导展示) */\r\nexport const AI_PRESET_PROVIDERS: AIPresetProvider[] = [\r\n { id: 'openai', name: 'OpenAI (GPT-4o)', baseUrl: 'https://api.openai.com/v1', defaultModel: 'gpt-4o-mini', requiresApiKey: true },\r\n { id: 'deepseek', name: 'DeepSeek', baseUrl: 'https://api.deepseek.com/v1', defaultModel: 'deepseek-chat', requiresApiKey: true },\r\n { id: 'moonshot', name: 'Moonshot (月之暗面)', baseUrl: 'https://api.moonshot.cn/v1', defaultModel: 'moonshot-v1-8k', requiresApiKey: true },\r\n { id: 'zhipu', name: '智谱 (GLM-4)', baseUrl: 'https://open.bigmodel.cn/api/paas/v4', defaultModel: 'glm-4-flash', requiresApiKey: true },\r\n { id: 'qwen', name: '通义千问 (Qwen)', baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', defaultModel: 'qwen-turbo', requiresApiKey: true },\r\n { id: 'ollama', name: 'Ollama (本地)', baseUrl: 'http://localhost:11434/v1', defaultModel: 'qwen2.5-coder:7b', requiresApiKey: false },\r\n];\r\n\r\n/**\r\n * 注册 AI Provider\r\n * @param name Provider 名称\r\n * @param ProviderClass Provider 类\r\n */\r\nexport function registerAIProvider(name: string, ProviderClass: AIProviderConstructor): void {\r\n providerRegistry.set(name, ProviderClass);\r\n}\r\n\r\n/**\r\n * 获取已注册的 Provider 名称列表\r\n */\r\nexport function getRegisteredProviders(): string[] {\r\n return Array.from(providerRegistry.keys());\r\n}\r\n\r\n/**\r\n * 获取 AI Provider 实例\r\n * @param config AI 配置,若未配置则返回 NoopProvider\r\n */\r\nexport function getAIProvider(config?: AIConfig): NoopAIProvider | OpenAICompatibleProvider {\r\n if (activeProvider) {\r\n return activeProvider;\r\n }\r\n\r\n if (!config || !config.provider) {\r\n activeProvider = new NoopAIProvider();\r\n return activeProvider;\r\n }\r\n\r\n const ProviderClass = providerRegistry.get(config.provider);\r\n if (!ProviderClass) {\r\n console.warn(\r\n `未找到 AI Provider \"${config.provider}\",已注册的 Provider: ${\r\n providerRegistry.size > 0 ? getRegisteredProviders().join(', ') : '无'\r\n }。将使用默认 Provider。`,\r\n );\r\n activeProvider = new NoopAIProvider();\r\n return activeProvider;\r\n }\r\n\r\n activeProvider = new ProviderClass(config);\r\n return activeProvider;\r\n}\r\n\r\n/**\r\n * 重置 Provider 实例(用于配置变更后重新初始化)\r\n */\r\nexport function resetAIProvider(): void {\r\n activeProvider = null;\r\n}\r\n\r\n/**\r\n * 检查 AI 功能是否可用\r\n */\r\nexport function isAIAvailable(config?: AIConfig): boolean {\r\n if (!config || !config.provider) return false;\r\n return providerRegistry.has(config.provider);\r\n}\r\n\r\n/**\r\n * 创建独立的 AI Provider 实例(不使用单例缓存)\r\n * 用于需要多个独立上下文的场景,如生成者+审计员双模型\r\n */\r\nexport function createAIProvider(config: AIConfig): NoopAIProvider | OpenAICompatibleProvider {\r\n if (!config || !config.provider) {\r\n return new NoopAIProvider();\r\n }\r\n\r\n const ProviderClass = providerRegistry.get(config.provider);\r\n if (!ProviderClass) {\r\n return new NoopAIProvider();\r\n }\r\n\r\n return new ProviderClass(config);\r\n}\r\n\r\n/**\r\n * 测试 AI 连通性\r\n */\r\nexport async function testAIConnection(config: AIConfig): Promise<{ ok: boolean; message: string; latencyMs?: number }> {\r\n const ProviderClass = providerRegistry.get(config.provider);\r\n if (!ProviderClass) {\r\n return { ok: false, message: `未注册的 Provider: ${config.provider},可选: ${getRegisteredProviders().join(', ')}` };\r\n }\r\n\r\n const provider = new ProviderClass(config);\r\n\r\n if (!(provider instanceof OpenAICompatibleProvider)) {\r\n return { ok: false, message: `${config.provider} 不支持连通性测试` };\r\n }\r\n\r\n return provider.testConnection();\r\n}\r\n\r\n// 重新导出类型\r\nexport type {\r\n AICapability,\r\n AIGenerateTestRequest,\r\n AIGenerateTestResponse,\r\n AIAnalyzeResultRequest,\r\n AIAnalyzeResultResponse,\r\n AIReviewTestRequest,\r\n AIReviewTestResponse,\r\n AIProvider,\r\n AIConfig,\r\n} from '../types/ai.js';\r\n","/**\r\n * Mock服务 - Express服务器,路由管理,数据模板\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport type { Request, Response, NextFunction } from 'express';\r\n\r\n/** Mock路由配置 */\r\nexport interface MockRoute {\r\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\r\n path: string;\r\n status?: number;\r\n response?: unknown;\r\n delay?: number;\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/** Mock服务器状态 */\r\nexport interface MockServerState {\r\n running: boolean;\r\n port: number;\r\n routes: MockRoute[];\r\n pid?: number;\r\n}\r\n\r\n/** 全局Mock服务器状态 */\r\nlet serverState: MockServerState = {\r\n running: false,\r\n port: 3456,\r\n routes: [],\r\n};\r\n\r\n/** 服务器实例引用 */\r\nlet serverInstance: ReturnType<typeof import('http').createServer> | null = null;\r\n\r\n/**\r\n * 获取Mock服务器状态\r\n */\r\nexport function getMockServerState(): MockServerState {\r\n return { ...serverState };\r\n}\r\n\r\n/**\r\n * 加载Mock路由配置\r\n * 支持从目录加载 .json 和 .js/.ts 路由文件\r\n */\r\nexport async function loadMockRoutes(routesDir: string): Promise<MockRoute[]> {\r\n const absDir = path.resolve(process.cwd(), routesDir);\r\n\r\n if (!fs.existsSync(absDir)) {\r\n return [];\r\n }\r\n\r\n const routes: MockRoute[] = [];\r\n const entries = fs.readdirSync(absDir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n if (!entry.isFile()) continue;\r\n\r\n const filePath = path.join(absDir, entry.name);\r\n const ext = path.extname(entry.name);\r\n\r\n try {\r\n if (ext === '.json') {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n const parsed = JSON.parse(content);\r\n if (Array.isArray(parsed)) {\r\n routes.push(...parsed);\r\n } else if (parsed.method && parsed.path) {\r\n routes.push(parsed as MockRoute);\r\n }\r\n } else if (ext === '.js' || ext === '.mjs') {\r\n const module = await import(filePath);\r\n const exported = module.default || module;\r\n if (Array.isArray(exported)) {\r\n routes.push(...exported);\r\n } else if (exported.method && exported.path) {\r\n routes.push(exported as MockRoute);\r\n }\r\n }\r\n } catch (error) {\r\n console.warn(`加载路由文件失败 ${entry.name}: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n }\r\n\r\n return routes;\r\n}\r\n\r\n/**\r\n * 创建默认Mock路由\r\n */\r\nexport function createDefaultRoutes(): MockRoute[] {\r\n return [\r\n {\r\n method: 'GET',\r\n path: '/api/health',\r\n status: 200,\r\n response: { status: 'ok', timestamp: Date.now() },\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/*',\r\n status: 200,\r\n response: { message: 'Mock API response', data: null },\r\n delay: 100,\r\n },\r\n {\r\n method: 'POST',\r\n path: '/api/*',\r\n status: 201,\r\n response: { message: 'Created successfully', data: null },\r\n },\r\n {\r\n method: 'PUT',\r\n path: '/api/*',\r\n status: 200,\r\n response: { message: 'Updated successfully', data: null },\r\n },\r\n {\r\n method: 'DELETE',\r\n path: '/api/*',\r\n status: 204,\r\n response: null,\r\n },\r\n ];\r\n}\r\n\r\n/**\r\n * 启动Mock服务器\r\n */\r\nexport async function startMockServer(port: number, routes: MockRoute[]): Promise<void> {\r\n if (serverState.running) {\r\n throw new Error(`Mock服务器已在运行 (端口: ${serverState.port})`);\r\n }\r\n\r\n const express = await import('express');\r\n const app = express.default();\r\n\r\n // 中间件\r\n app.use(express.default.json());\r\n\r\n // 请求日志\r\n app.use((req: Request, _res: Response, next: NextFunction) => {\r\n if (process.env.QAT_VERBOSE === 'true') {\r\n console.log(` [Mock] ${req.method} ${req.url}`);\r\n }\r\n next();\r\n });\r\n\r\n // 注册路由\r\n const allRoutes = routes.length > 0 ? routes : createDefaultRoutes();\r\n\r\n for (const route of allRoutes) {\r\n const handler = async (req: Request, res: Response) => {\r\n // 模拟延迟\r\n if (route.delay && route.delay > 0) {\r\n await new Promise((resolve) => setTimeout(resolve, route.delay));\r\n }\r\n\r\n // 设置自定义响应头\r\n if (route.headers) {\r\n for (const [key, value] of Object.entries(route.headers)) {\r\n res.setHeader(key, value);\r\n }\r\n }\r\n\r\n res.setHeader('X-Mock-Server', 'qat');\r\n\r\n // 替换响应中的请求参数\r\n let response = route.response;\r\n if (typeof response === 'object' && response !== null) {\r\n const responseStr = JSON.stringify(response)\r\n .replace(/\\{\\{params\\.(\\w+)\\}\\}/g, (_match, key: string) => (req.params[key] as string) || '')\r\n .replace(/\\{\\{query\\.(\\w+)\\}\\}/g, (_match, key: string) => (req.query[key] as string) || '')\r\n .replace(/\\{\\{body\\.(\\w+)\\}\\}/g, (_match, key: string) => String((req.body as Record<string, unknown>)?.[key] ?? ''));\r\n try {\r\n response = JSON.parse(responseStr);\r\n } catch {\r\n // 保持原始响应\r\n }\r\n }\r\n\r\n res.status(route.status || 200).json(response);\r\n };\r\n\r\n // 根据方法注册路由\r\n const expressRoute = route.path;\r\n switch (route.method) {\r\n case 'GET':\r\n app.get(expressRoute, handler);\r\n break;\r\n case 'POST':\r\n app.post(expressRoute, handler);\r\n break;\r\n case 'PUT':\r\n app.put(expressRoute, handler);\r\n break;\r\n case 'DELETE':\r\n app.delete(expressRoute, handler);\r\n break;\r\n case 'PATCH':\r\n app.patch(expressRoute, handler);\r\n break;\r\n }\r\n }\r\n\r\n // 404 兜底\r\n app.use((req, res) => {\r\n res.status(404).json({\r\n error: 'Not Found',\r\n message: `No mock route defined for ${req.method} ${req.url}`,\r\n hint: '在 tests/mock/routes/ 目录下添加路由配置',\r\n });\r\n });\r\n\r\n // 启动服务器\r\n return new Promise((resolve, reject) => {\r\n serverInstance = app.listen(port, () => {\r\n serverState = {\r\n running: true,\r\n port,\r\n routes: allRoutes,\r\n pid: process.pid,\r\n };\r\n resolve();\r\n });\r\n\r\n serverInstance.on('error', (err: Error) => {\r\n reject(new Error(`Mock服务器启动失败: ${err.message}`));\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 停止Mock服务器\r\n */\r\nexport async function stopMockServer(): Promise<void> {\r\n if (!serverInstance || !serverState.running) {\r\n serverState = { ...serverState, running: false };\r\n return;\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n serverInstance!.close((err) => {\r\n if (err) {\r\n reject(new Error(`Mock服务器停止失败: ${err.message}`));\r\n return;\r\n }\r\n\r\n serverInstance = null;\r\n serverState = {\r\n running: false,\r\n port: serverState.port,\r\n routes: [],\r\n };\r\n resolve();\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 生成Mock路由配置文件模板\r\n */\r\nexport function generateMockRouteTemplate(name: string): string {\r\n return `[\r\n {\r\n method: 'GET',\r\n path: '/api/${name}',\r\n status: 200,\r\n response: {\r\n message: 'Success',\r\n data: {\r\n id: 1,\r\n name: '${name}',\r\n },\r\n },\r\n delay: 100,\r\n },\r\n {\r\n method: 'POST',\r\n path: '/api/${name}',\r\n status: 201,\r\n response: {\r\n message: 'Created',\r\n data: {\r\n id: 2,\r\n name: '{{body.name}}',\r\n },\r\n },\r\n },\r\n {\r\n method: 'DELETE',\r\n path: '/api/${name}/:id',\r\n status: 204,\r\n response: null,\r\n },\r\n]\r\n`;\r\n}\r\n\r\n/**\r\n * 初始化Mock路由目录\r\n */\r\nexport function initMockRoutesDir(routesDir: string): void {\r\n const absDir = path.resolve(process.cwd(), routesDir);\r\n\r\n if (!fs.existsSync(absDir)) {\r\n fs.mkdirSync(absDir, { recursive: true });\r\n }\r\n\r\n // 生成示例路由文件\r\n const examplePath = path.join(absDir, 'example.json');\r\n if (!fs.existsSync(examplePath)) {\r\n const exampleRoutes: MockRoute[] = [\r\n {\r\n method: 'GET',\r\n path: '/api/users',\r\n status: 200,\r\n response: {\r\n message: 'Success',\r\n data: [\r\n { id: 1, name: 'Alice' },\r\n { id: 2, name: 'Bob' },\r\n ],\r\n },\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/users/:id',\r\n status: 200,\r\n response: {\r\n message: 'Success',\r\n data: { id: 1, name: 'Alice' },\r\n },\r\n },\r\n ];\r\n fs.writeFileSync(examplePath, JSON.stringify(exampleRoutes, null, 2), 'utf-8');\r\n }\r\n}\r\n","/**\r\n * 视觉回归服务 - 截图比对、基线管理、差异图片生成\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport pixelmatch from 'pixelmatch';\r\nimport { PNG } from 'pngjs';\r\n\r\n/** 比对结果 */\r\nexport interface DiffResult {\r\n /** 是否通过(差异在阈值内) */\r\n passed: boolean;\r\n /** 差异像素数 */\r\n diffPixels: number;\r\n /** 总像素数 */\r\n totalPixels: number;\r\n /** 差异比例 (0-1) */\r\n diffRatio: number;\r\n /** 基线图片路径 */\r\n baselinePath: string;\r\n /** 当前图片路径 */\r\n currentPath: string;\r\n /** 差异图片路径(仅在有差异时生成) */\r\n diffPath?: string;\r\n}\r\n\r\n/**\r\n * 比对两张图片\r\n */\r\nexport function compareImages(\r\n baselinePath: string,\r\n currentPath: string,\r\n diffOutputPath: string,\r\n threshold: number = 0.1,\r\n): DiffResult {\r\n // 读取基线图片\r\n if (!fs.existsSync(baselinePath)) {\r\n throw new Error(`基线图片不存在: ${baselinePath}`);\r\n }\r\n\r\n if (!fs.existsSync(currentPath)) {\r\n throw new Error(`当前图片不存在: ${currentPath}`);\r\n }\r\n\r\n const baseline = PNG.sync.read(fs.readFileSync(baselinePath));\r\n const current = PNG.sync.read(fs.readFileSync(currentPath));\r\n\r\n // 尺寸必须一致\r\n if (baseline.width !== current.width || baseline.height !== current.height) {\r\n return {\r\n passed: false,\r\n diffPixels: -1,\r\n totalPixels: baseline.width * baseline.height,\r\n diffRatio: 1,\r\n baselinePath,\r\n currentPath,\r\n diffPath: undefined,\r\n };\r\n }\r\n\r\n const { width, height } = baseline;\r\n const totalPixels = width * height;\r\n\r\n // 创建差异图片\r\n const diff = new PNG({ width, height });\r\n const diffPixels = pixelmatch(\r\n baseline.data,\r\n current.data,\r\n diff.data,\r\n width,\r\n height,\r\n { threshold: 0.1 }, // pixelmatch 自身阈值(颜色差异灵敏度)\r\n );\r\n\r\n const diffRatio = diffPixels / totalPixels;\r\n const passed = diffRatio <= threshold;\r\n\r\n // 生成差异图片(仅在有差异时)\r\n let diffPath: string | undefined;\r\n if (diffPixels > 0) {\r\n // 确保输出目录存在\r\n const diffDir = path.dirname(diffOutputPath);\r\n if (!fs.existsSync(diffDir)) {\r\n fs.mkdirSync(diffDir, { recursive: true });\r\n }\r\n fs.writeFileSync(diffOutputPath, PNG.sync.write(diff));\r\n diffPath = diffOutputPath;\r\n }\r\n\r\n return {\r\n passed,\r\n diffPixels,\r\n totalPixels,\r\n diffRatio,\r\n baselinePath,\r\n currentPath,\r\n diffPath,\r\n };\r\n}\r\n\r\n/**\r\n * 创建基线快照(如果不存在则从当前截图复制)\r\n */\r\nexport function createBaseline(\r\n currentPath: string,\r\n baselinePath: string,\r\n): string {\r\n if (!fs.existsSync(currentPath)) {\r\n throw new Error(`当前截图不存在: ${currentPath}`);\r\n }\r\n\r\n const baselineDir = path.dirname(baselinePath);\r\n if (!fs.existsSync(baselineDir)) {\r\n fs.mkdirSync(baselineDir, { recursive: true });\r\n }\r\n\r\n fs.copyFileSync(currentPath, baselinePath);\r\n return baselinePath;\r\n}\r\n\r\n/**\r\n * 更新基线(将当前截图替换为基线)\r\n */\r\nexport function updateBaseline(\r\n currentPath: string,\r\n baselinePath: string,\r\n): string {\r\n return createBaseline(currentPath, baselinePath);\r\n}\r\n\r\n/**\r\n * 批量更新基线(将 diff 目录下的所有当前截图更新为基线)\r\n */\r\nexport function updateAllBaselines(\r\n currentDir: string,\r\n baselineDir: string,\r\n): string[] {\r\n const updated: string[] = [];\r\n\r\n if (!fs.existsSync(currentDir)) {\r\n return updated;\r\n }\r\n\r\n const files = fs.readdirSync(currentDir).filter((f) => f.endsWith('.png'));\r\n\r\n for (const file of files) {\r\n const currentPath = path.join(currentDir, file);\r\n const baselinePath = path.join(baselineDir, file);\r\n\r\n const baselineDirAbs = path.dirname(baselinePath);\r\n if (!fs.existsSync(baselineDirAbs)) {\r\n fs.mkdirSync(baselineDirAbs, { recursive: true });\r\n }\r\n\r\n fs.copyFileSync(currentPath, baselinePath);\r\n updated.push(file);\r\n }\r\n\r\n return updated;\r\n}\r\n\r\n/**\r\n * 清理所有基线快照\r\n */\r\nexport function cleanBaselines(baselineDir: string): number {\r\n if (!fs.existsSync(baselineDir)) {\r\n return 0;\r\n }\r\n\r\n const files = fs.readdirSync(baselineDir).filter((f) => f.endsWith('.png'));\r\n let count = 0;\r\n\r\n for (const file of files) {\r\n fs.unlinkSync(path.join(baselineDir, file));\r\n count++;\r\n }\r\n\r\n return count;\r\n}\r\n\r\n/**\r\n * 清理差异图片\r\n */\r\nexport function cleanDiffs(diffDir: string): number {\r\n if (!fs.existsSync(diffDir)) {\r\n return 0;\r\n }\r\n\r\n const files = fs.readdirSync(diffDir).filter((f) => f.endsWith('.png'));\r\n let count = 0;\r\n\r\n for (const file of files) {\r\n fs.unlinkSync(path.join(diffDir, file));\r\n count++;\r\n }\r\n\r\n return count;\r\n}\r\n\r\n/**\r\n * 获取基线快照列表\r\n */\r\nexport function listBaselines(baselineDir: string): string[] {\r\n if (!fs.existsSync(baselineDir)) {\r\n return [];\r\n }\r\n\r\n return fs\r\n .readdirSync(baselineDir)\r\n .filter((f) => f.endsWith('.png'))\r\n .sort();\r\n}\r\n\r\n/**\r\n * 获取差异图片列表\r\n */\r\nexport function listDiffs(diffDir: string): string[] {\r\n if (!fs.existsSync(diffDir)) {\r\n return [];\r\n }\r\n\r\n return fs\r\n .readdirSync(diffDir)\r\n .filter((f) => f.endsWith('.png'))\r\n .sort();\r\n}\r\n\r\n/**\r\n * 批量比对目录中的所有截图\r\n */\r\nexport function compareDirectories(\r\n baselineDir: string,\r\n currentDir: string,\r\n diffDir: string,\r\n threshold: number = 0.1,\r\n): DiffResult[] {\r\n const results: DiffResult[] = [];\r\n\r\n if (!fs.existsSync(currentDir)) {\r\n return results;\r\n }\r\n\r\n const currentFiles = fs.readdirSync(currentDir).filter((f) => f.endsWith('.png'));\r\n\r\n for (const file of currentFiles) {\r\n const currentPath = path.join(currentDir, file);\r\n const baselinePath = path.join(baselineDir, file);\r\n const diffPath = path.join(diffDir, file);\r\n\r\n if (!fs.existsSync(baselinePath)) {\r\n // 无基线,自动创建\r\n createBaseline(currentPath, baselinePath);\r\n results.push({\r\n passed: true,\r\n diffPixels: 0,\r\n totalPixels: 0,\r\n diffRatio: 0,\r\n baselinePath,\r\n currentPath,\r\n diffPath: undefined,\r\n });\r\n continue;\r\n }\r\n\r\n try {\r\n const result = compareImages(baselinePath, currentPath, diffPath, threshold);\r\n results.push(result);\r\n } catch (error) {\r\n results.push({\r\n passed: false,\r\n diffPixels: -1,\r\n totalPixels: 0,\r\n diffRatio: 1,\r\n baselinePath,\r\n currentPath,\r\n diffPath: undefined,\r\n });\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n","/**\r\n * 源码分析器 - 扫描 TS/JS/Vue 文件,提取导出、Props、Events、API调用 等信息\r\n * 用于生成个性化的测试用例和 Mock 路由\r\n */\r\n\r\nimport fs from 'node:fs';\r\nimport path from 'node:path';\r\n\r\n// ─── 分析结果类型 ────────────────────────────────────────────\r\n\r\n/** 导出项类型 */\r\nexport type ExportKind = 'function' | 'class' | 'const' | 'type' | 'default' | 'component';\r\n\r\n/** 导出项信息 */\r\nexport interface ExportInfo {\r\n /** 导出名称 */\r\n name: string;\r\n /** 导出类型 */\r\n kind: ExportKind;\r\n /** 函数参数列表 */\r\n params: string[];\r\n /** 函数是否有返回值类型注解 */\r\n returnType?: string;\r\n /** 是否为异步函数 */\r\n isAsync: boolean;\r\n}\r\n\r\n/** Vue 组件 Props 信息 */\r\nexport interface PropInfo {\r\n /** prop 名称 */\r\n name: string;\r\n /** prop 类型(如 String, Number, Boolean, Array, Object) */\r\n type: string;\r\n /** 是否必填 */\r\n required: boolean;\r\n /** 默认值 */\r\n defaultValue?: string;\r\n}\r\n\r\n/** Vue 组件 Emits 信息 */\r\nexport interface EmitInfo {\r\n /** 事件名称 */\r\n name: string;\r\n /** 事件参数 */\r\n params: string[];\r\n}\r\n\r\n/** Vue 组件分析结果 */\r\nexport interface VueComponentAnalysis {\r\n /** 组件名称 */\r\n componentName: string;\r\n /** Props 列表 */\r\n props: PropInfo[];\r\n /** Emits 列表 */\r\n emits: EmitInfo[];\r\n /** 组件中定义的方法名 */\r\n methods: string[];\r\n /** 组件中定义的 computed 名 */\r\n computed: string[];\r\n /** 是否使用 setup 语法 */\r\n usesSetup: boolean;\r\n}\r\n\r\n/** API 调用信息 */\r\nexport interface APICallInfo {\r\n /** HTTP 方法 */\r\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\r\n /** 请求路径 */\r\n url: string;\r\n /** 来源文件 */\r\n sourceFile: string;\r\n}\r\n\r\n/** TS/JS 文件分析结果 */\r\nexport interface ModuleAnalysis {\r\n /** 文件路径 */\r\n filePath: string;\r\n /** 导出项列表 */\r\n exports: ExportInfo[];\r\n /** Vue 组件分析(仅 .vue 文件) */\r\n vueAnalysis?: VueComponentAnalysis;\r\n /** 发现的 API 调用 */\r\n apiCalls: APICallInfo[];\r\n}\r\n\r\n// ─── 主分析函数 ──────────────────────────────────────────────\r\n\r\n/**\r\n * 分析一个源码文件\r\n */\r\nexport function analyzeFile(filePath: string): ModuleAnalysis {\r\n const absolutePath = path.resolve(process.cwd(), filePath);\r\n\r\n if (!fs.existsSync(absolutePath)) {\r\n return { filePath, exports: [], apiCalls: [] };\r\n }\r\n\r\n const content = fs.readFileSync(absolutePath, 'utf-8');\r\n const ext = path.extname(filePath);\r\n\r\n const result: ModuleAnalysis = {\r\n filePath,\r\n exports: extractExports(content, ext),\r\n apiCalls: extractAPICalls(content, filePath),\r\n };\r\n\r\n // Vue 组件分析\r\n if (ext === '.vue') {\r\n result.vueAnalysis = analyzeVueComponent(content, path.basename(filePath, '.vue'));\r\n // Vue 文件的 <script> 部分也提取 API 调用\r\n const scriptMatch = content.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\r\n if (scriptMatch) {\r\n result.apiCalls = extractAPICalls(scriptMatch[1], filePath);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 批量分析文件\r\n */\r\nexport function analyzeFiles(filePaths: string[]): ModuleAnalysis[] {\r\n return filePaths.map(analyzeFile);\r\n}\r\n\r\n// ─── 导出提取 ────────────────────────────────────────────────\r\n\r\n/**\r\n * 从源码中提取导出项\r\n */\r\nfunction extractExports(content: string, ext: string): ExportInfo[] {\r\n const exports: ExportInfo[] = [];\r\n\r\n // 命名导出: export function foo() / export const bar / export class Baz\r\n const namedExportRegex = /export\\s+(async\\s+)?function\\s+(\\w+)\\s*\\(([^)]*)\\)(\\s*:\\s*([^{]+))?/g;\r\n let match: RegExpExecArray | null;\r\n\r\n while ((match = namedExportRegex.exec(content)) !== null) {\r\n exports.push({\r\n name: match[2],\r\n kind: 'function',\r\n params: parseParams(match[3]),\r\n returnType: match[5]?.trim(),\r\n isAsync: !!match[1],\r\n });\r\n }\r\n\r\n // export const/let/var\r\n const constExportRegex = /export\\s+(const|let|var)\\s+(\\w+)\\s*(?::\\s*([^{=]+))?\\s*[={]/g;\r\n while ((match = constExportRegex.exec(content)) !== null) {\r\n const typeAnnotation = match[3]?.trim();\r\n const kind: ExportKind = typeAnnotation === 'DefineComponent' || isComponentType(typeAnnotation)\r\n ? 'component'\r\n : 'const';\r\n\r\n exports.push({\r\n name: match[2],\r\n kind,\r\n params: [],\r\n returnType: typeAnnotation,\r\n isAsync: false,\r\n });\r\n }\r\n\r\n // export class\r\n const classExportRegex = /export\\s+class\\s+(\\w+)/g;\r\n while ((match = classExportRegex.exec(content)) !== null) {\r\n exports.push({\r\n name: match[1],\r\n kind: 'class',\r\n params: [],\r\n isAsync: false,\r\n });\r\n }\r\n\r\n // export type/interface\r\n const typeExportRegex = /export\\s+(type|interface)\\s+(\\w+)/g;\r\n while ((match = typeExportRegex.exec(content)) !== null) {\r\n exports.push({\r\n name: match[2],\r\n kind: 'type',\r\n params: [],\r\n isAsync: false,\r\n });\r\n }\r\n\r\n // export default\r\n if (/export\\s+default\\s+/.test(content)) {\r\n // export default function\r\n const defaultFuncMatch = /export\\s+default\\s+(async\\s+)?function\\s+(\\w*)\\s*\\(([^)]*)\\)/.exec(content);\r\n if (defaultFuncMatch) {\r\n exports.push({\r\n name: defaultFuncMatch[2] || 'default',\r\n kind: 'default',\r\n params: parseParams(defaultFuncMatch[3]),\r\n isAsync: !!defaultFuncMatch[1],\r\n });\r\n } else {\r\n // export default expression\r\n exports.push({\r\n name: 'default',\r\n kind: ext === '.vue' ? 'component' : 'default',\r\n params: [],\r\n isAsync: false,\r\n });\r\n }\r\n }\r\n\r\n // re-export: export { foo, bar } from './module'\r\n // 这些我们不需要生成测试\r\n\r\n return exports;\r\n}\r\n\r\n/**\r\n * 解析函数参数字符串\r\n */\r\nfunction parseParams(paramsStr: string): string[] {\r\n if (!paramsStr.trim()) return [];\r\n\r\n return paramsStr\r\n .split(',')\r\n .map((p) => {\r\n // 提取参数名(去掉类型注解和默认值)\r\n const trimmed = p.trim();\r\n const nameMatch = trimmed.match(/(?:\\.\\.\\.)?(\\w+)/);\r\n return nameMatch ? nameMatch[1] : trimmed;\r\n })\r\n .filter((p) => p && p !== '_' && !p.startsWith('__'));\r\n}\r\n\r\n/**\r\n * 判断是否为组件类型\r\n */\r\nfunction isComponentType(typeStr?: string): boolean {\r\n if (!typeStr) return false;\r\n return /DefineComponent|FunctionalComponent|Component\\b/i.test(typeStr);\r\n}\r\n\r\n// ─── Vue 组件分析 ────────────────────────────────────────────\r\n\r\n/**\r\n * 分析 Vue 组件\r\n */\r\nfunction analyzeVueComponent(content: string, componentName: string): VueComponentAnalysis {\r\n const analysis: VueComponentAnalysis = {\r\n componentName,\r\n props: [],\r\n emits: [],\r\n methods: [],\r\n computed: [],\r\n usesSetup: false,\r\n };\r\n\r\n // 检测是否使用 <script setup>\r\n analysis.usesSetup = /<script[^>]*setup[^>]*>/.test(content);\r\n\r\n // 提取 <script> 部分内容\r\n const scriptMatch = content.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);\r\n if (!scriptMatch) return analysis;\r\n\r\n const scriptContent = scriptMatch[1];\r\n\r\n // 解析 Props\r\n analysis.props = extractProps(scriptContent, analysis.usesSetup);\r\n\r\n // 解析 Emits\r\n analysis.emits = extractEmits(scriptContent, analysis.usesSetup);\r\n\r\n // 解析 Methods(仅 Options API)\r\n if (!analysis.usesSetup) {\r\n analysis.methods = extractMethods(scriptContent);\r\n analysis.computed = extractComputed(scriptContent);\r\n }\r\n\r\n return analysis;\r\n}\r\n\r\n/**\r\n * 提取 Props\r\n */\r\nfunction extractProps(scriptContent: string, usesSetup: boolean): PropInfo[] {\r\n const props: PropInfo[] = [];\r\n\r\n if (usesSetup) {\r\n // defineProps 的对象形式: defineProps({ foo: { type: String, required: true } })\r\n const definePropsObjMatch = scriptContent.match(/defineProps\\s*<[^>]*>\\s*\\(\\s*\\)|defineProps\\s*\\(\\s*\\{([\\s\\S]*?)\\}\\s*\\)/);\r\n if (definePropsObjMatch && definePropsObjMatch[1]) {\r\n const objContent = definePropsObjMatch[1];\r\n // 匹配每个 prop 定义\r\n const propRegex = /(\\w+)\\s*:\\s*\\{([^}]*)\\}/g;\r\n let propMatch: RegExpExecArray | null;\r\n while ((propMatch = propRegex.exec(objContent)) !== null) {\r\n const propBody = propMatch[2];\r\n props.push({\r\n name: propMatch[1],\r\n type: extractPropType(propBody),\r\n required: /required\\s*:\\s*true/.test(propBody),\r\n defaultValue: extractPropDefault(propBody),\r\n });\r\n }\r\n }\r\n\r\n // defineProps 的数组形式: defineProps(['foo', 'bar'])\r\n const definePropsArrMatch = scriptContent.match(/defineProps\\s*\\(\\s*\\[([^\\]]*)\\]\\s*\\)/);\r\n if (definePropsArrMatch && definePropsArrMatch[1]) {\r\n const names = definePropsArrMatch[1].match(/'([^']+)'/g);\r\n if (names) {\r\n for (const name of names) {\r\n const cleanName = name.replace(/'/g, '');\r\n if (!props.some((p) => p.name === cleanName)) {\r\n props.push({\r\n name: cleanName,\r\n type: 'any',\r\n required: false,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // defineProps 的类型形式: defineProps<{ foo: string, bar?: number }>()\r\n const typePropsMatch = scriptContent.match(/defineProps\\s*<\\s*\\{([^}]*)\\}/);\r\n if (typePropsMatch && typePropsMatch[1] && props.length === 0) {\r\n const typeContent = typePropsMatch[1];\r\n const typePropRegex = /(\\??\\s*)(\\w+)\\s*:\\s*([^;,\\n]+)/g;\r\n let typeMatch: RegExpExecArray | null;\r\n while ((typeMatch = typePropRegex.exec(typeContent)) !== null) {\r\n props.push({\r\n name: typeMatch[2],\r\n type: typeMatch[3].trim(),\r\n required: !typeMatch[1].includes('?'),\r\n });\r\n }\r\n }\r\n } else {\r\n // Options API: props: { ... }\r\n const propsObjMatch = scriptContent.match(/props\\s*:\\s*\\{([\\s\\S]*?)\\}\\s*,?\\s*(?:emits|data|computed|methods|setup|components|watch|$)/);\r\n if (propsObjMatch && propsObjMatch[1]) {\r\n const propRegex = /(\\w+)\\s*:\\s*\\{([^}]*)\\}/g;\r\n let propMatch: RegExpExecArray | null;\r\n while ((propMatch = propRegex.exec(propsObjMatch[1])) !== null) {\r\n const propBody = propMatch[2];\r\n props.push({\r\n name: propMatch[1],\r\n type: extractPropType(propBody),\r\n required: /required\\s*:\\s*true/.test(propBody),\r\n defaultValue: extractPropDefault(propBody),\r\n });\r\n }\r\n }\r\n\r\n // Array 形式: props: ['foo', 'bar']\r\n const propsArrMatch = scriptContent.match(/props\\s*:\\s*\\[([^\\]]*)\\]/);\r\n if (propsArrMatch && propsArrMatch[1]) {\r\n const names = propsArrMatch[1].match(/'([^']+)'/g);\r\n if (names) {\r\n for (const name of names) {\r\n const cleanName = name.replace(/'/g, '');\r\n if (!props.some((p) => p.name === cleanName)) {\r\n props.push({\r\n name: cleanName,\r\n type: 'any',\r\n required: false,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return props;\r\n}\r\n\r\n/**\r\n * 提取 prop 类型\r\n */\r\nfunction extractPropType(propBody: string): string {\r\n const typeMatch = propBody.match(/type\\s*:\\s*(\\w+)/);\r\n if (typeMatch) return typeMatch[1];\r\n return 'any';\r\n}\r\n\r\n/**\r\n * 提取 prop 默认值\r\n */\r\nfunction extractPropDefault(propBody: string): string | undefined {\r\n const defaultMatch = propBody.match(/default\\s*:\\s*([^,}\\n]+)/);\r\n if (defaultMatch) return defaultMatch[1].trim();\r\n return undefined;\r\n}\r\n\r\n/**\r\n * 提取 Emits\r\n */\r\nfunction extractEmits(scriptContent: string, usesSetup: boolean): EmitInfo[] {\r\n const emits: EmitInfo[] = [];\r\n\r\n if (usesSetup) {\r\n // defineEmits(['foo', 'bar'])\r\n const arrMatch = scriptContent.match(/defineEmits\\s*\\(\\s*\\[([^\\]]*)\\]\\s*\\)/);\r\n if (arrMatch && arrMatch[1]) {\r\n const names = arrMatch[1].match(/'([^']+)'/g);\r\n if (names) {\r\n for (const name of names) {\r\n emits.push({\r\n name: name.replace(/'/g, ''),\r\n params: [],\r\n });\r\n }\r\n }\r\n }\r\n\r\n // defineEmits<{ foo: [...], bar: [...] }>()\r\n const typeMatch = scriptContent.match(/defineEmits\\s*<\\s*\\{([^}]*)\\}/);\r\n if (typeMatch && typeMatch[1] && emits.length === 0) {\r\n const emitRegex = /(\\w+)\\s*:\\s*\\(([^)]*)\\)/g;\r\n let emitMatch: RegExpExecArray | null;\r\n while ((emitMatch = emitRegex.exec(typeMatch[1])) !== null) {\r\n emits.push({\r\n name: emitMatch[1],\r\n params: parseParams(emitMatch[2]),\r\n });\r\n }\r\n }\r\n } else {\r\n // Options API: emits: ['foo', 'bar'] or emits: { ... }\r\n const arrMatch = scriptContent.match(/emits\\s*:\\s*\\[([^\\]]*)\\]/);\r\n if (arrMatch && arrMatch[1]) {\r\n const names = arrMatch[1].match(/'([^']+)'/g);\r\n if (names) {\r\n for (const name of names) {\r\n emits.push({\r\n name: name.replace(/'/g, ''),\r\n params: [],\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n return emits;\r\n}\r\n\r\n/**\r\n * 提取 Options API 方法\r\n */\r\nfunction extractMethods(scriptContent: string): string[] {\r\n const methods: string[] = [];\r\n const methodsMatch = scriptContent.match(/methods\\s*:\\s*\\{([\\s\\S]*?)\\}\\s*,?\\s*(?:computed|watch|components|mounted|created|setup|$)/);\r\n if (methodsMatch && methodsMatch[1]) {\r\n const methodRegex = /(?:async\\s+)?(\\w+)\\s*\\(/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = methodRegex.exec(methodsMatch[1])) !== null) {\r\n const name = match[1];\r\n if (name !== 'constructor' && !methods.includes(name)) {\r\n methods.push(name);\r\n }\r\n }\r\n }\r\n return methods;\r\n}\r\n\r\n/**\r\n * 提取 computed 属性\r\n */\r\nfunction extractComputed(scriptContent: string): string[] {\r\n const computed: string[] = [];\r\n const computedMatch = scriptContent.match(/computed\\s*:\\s*\\{([\\s\\S]*?)\\}\\s*,?\\s*(?:methods|watch|components|mounted|created|setup|$)/);\r\n if (computedMatch && computedMatch[1]) {\r\n const propRegex = /(\\w+)\\s*(?:\\(\\)|\\{)/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = propRegex.exec(computedMatch[1])) !== null) {\r\n const name = match[1];\r\n if (!computed.includes(name)) {\r\n computed.push(name);\r\n }\r\n }\r\n }\r\n return computed;\r\n}\r\n\r\n// ─── API 调用提取 ────────────────────────────────────────────\r\n\r\n/**\r\n * 从源码中提取 API 调用信息\r\n */\r\nfunction extractAPICalls(content: string, sourceFile: string): APICallInfo[] {\r\n const calls: APICallInfo[] = [];\r\n const seen = new Set<string>();\r\n\r\n // 1. fetch('url', { method: 'POST' }) 或 fetch(`url`)\r\n const fetchRegex = /fetch\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = fetchRegex.exec(content)) !== null) {\r\n const url = cleanTemplateUrl(match[1]);\r\n // 检查同一行附近是否有 method 指定\r\n const afterFetch = content.slice(match.index, match.index + 300);\r\n const methodMatch = afterFetch.match(/method\\s*:\\s*['\"]?(GET|POST|PUT|DELETE|PATCH)['\"]?/i);\r\n const method = methodMatch ? methodMatch[1].toUpperCase() as APICallInfo['method'] : 'GET';\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n // 2. axios.get('url') / axios.post('url') / axios.put / axios.delete / axios.patch\r\n const axiosRegex = /axios\\s*\\.\\s*(get|post|put|delete|patch)\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/gi;\r\n while ((match = axiosRegex.exec(content)) !== null) {\r\n const method = match[1].toUpperCase() as APICallInfo['method'];\r\n const url = cleanTemplateUrl(match[2]);\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n // 3. useFetch('url') / useFetch('url', { method: 'POST' }) (Nuxt/composable)\r\n const useFetchRegex = /useFetch\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/g;\r\n while ((match = useFetchRegex.exec(content)) !== null) {\r\n const url = cleanTemplateUrl(match[1]);\r\n const afterUseFetch = content.slice(match.index, match.index + 300);\r\n const methodMatch = afterUseFetch.match(/method\\s*:\\s*['\"]?(GET|POST|PUT|DELETE|PATCH)['\"]?/i);\r\n const method = methodMatch ? methodMatch[1].toUpperCase() as APICallInfo['method'] : 'GET';\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n // 4. $fetch('url', { method: 'POST' }) (Nuxt)\r\n const dollarFetchRegex = /\\$fetch\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/g;\r\n while ((match = dollarFetchRegex.exec(content)) !== null) {\r\n const url = cleanTemplateUrl(match[1]);\r\n const afterFetch = content.slice(match.index, match.index + 300);\r\n const methodMatch = afterFetch.match(/method\\s*:\\s*['\"]?(GET|POST|PUT|DELETE|PATCH)['\"]?/i);\r\n const method = methodMatch ? methodMatch[1].toUpperCase() as APICallInfo['method'] : 'GET';\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n // 5. httpRequest.get('url') / http.post('url') 等自定义实例\r\n const httpRegex = /\\w+\\s*\\.\\s*(get|post|put|delete|patch)\\s*\\(\\s*[`'\"]([^`'\"]+)[`'\"]/gi;\r\n while ((match = httpRegex.exec(content)) !== null) {\r\n // 排除 axios 已匹配的\r\n const fullLine = content.slice(Math.max(0, match.index - 20), match.index + match[0].length);\r\n if (/axios/i.test(fullLine)) continue;\r\n const method = match[1].toUpperCase() as APICallInfo['method'];\r\n const url = cleanTemplateUrl(match[2]);\r\n const key = `${method}:${url}`;\r\n if (!seen.has(key) && url.startsWith('/')) {\r\n seen.add(key);\r\n calls.push({ method, url, sourceFile });\r\n }\r\n }\r\n\r\n return calls;\r\n}\r\n\r\n/**\r\n * 清理模板 URL 中的变量插值\r\n * `/api/users/${id}` → `/api/users/:id`\r\n */\r\nfunction cleanTemplateUrl(url: string): string {\r\n return url\r\n .replace(/\\$\\{[^}]*\\}/g, ':param')\r\n .replace(/\\/+/g, '/')\r\n .replace(/:param/g, (_match, offset, str) => {\r\n // 尝试从前面的路径推断参数名\r\n const before = str.slice(Math.max(0, offset - 20), offset);\r\n const nameMatch = before.match(/(\\w+)Id|by(\\w+)|(\\w+)Id/i);\r\n if (nameMatch) {\r\n const name = (nameMatch[1] || nameMatch[2] || nameMatch[3] || 'param').toLowerCase();\r\n return `:${name}`;\r\n }\r\n return ':id';\r\n });\r\n}\r\n\r\n// ─── 扫描目录提取 API ────────────────────────────────────────\r\n\r\n/**\r\n * 扫描项目源码目录,提取所有 API 调用\r\n */\r\nexport function scanAPICalls(srcDir: string): APICallInfo[] {\r\n const absDir = path.resolve(process.cwd(), srcDir);\r\n\r\n if (!fs.existsSync(absDir)) {\r\n return [];\r\n }\r\n\r\n const allCalls: APICallInfo[] = [];\r\n const seen = new Set<string>();\r\n\r\n function walk(dir: string): void {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n for (const entry of entries) {\r\n // 跳过 node_modules, dist, .git 等\r\n if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist') continue;\r\n\r\n const fullPath = path.join(dir, entry.name);\r\n if (entry.isDirectory()) {\r\n walk(fullPath);\r\n } else if (entry.isFile() && /\\.(ts|js|vue|mjs)$/.test(entry.name)) {\r\n try {\r\n const content = fs.readFileSync(fullPath, 'utf-8');\r\n const calls = extractAPICalls(content, path.relative(process.cwd(), fullPath));\r\n for (const call of calls) {\r\n const key = `${call.method}:${call.url}`;\r\n if (!seen.has(key)) {\r\n seen.add(key);\r\n allCalls.push(call);\r\n }\r\n }\r\n } catch {\r\n // 忽略读取失败\r\n }\r\n }\r\n }\r\n }\r\n\r\n walk(absDir);\r\n return allCalls;\r\n}\r\n\r\n/**\r\n * 根据 API 调用信息自动生成 Mock 路由配置\r\n */\r\nexport function generateMockRoutesFromAPICalls(apiCalls: APICallInfo[]): MockRouteCandidate[] {\r\n const routes: MockRouteCandidate[] = [];\r\n\r\n for (const call of apiCalls) {\r\n // 从 URL 路径推断资源名\r\n const segments = call.url.split('/').filter(Boolean);\r\n const resourceName = segments[segments.length - 1]?.replace(/^:/, '') || 'resource';\r\n\r\n let response: Record<string, unknown> | null;\r\n let status = 200;\r\n\r\n switch (call.method) {\r\n case 'GET':\r\n if (call.url.includes(':')) {\r\n // 单个资源\r\n response = { message: 'Success', data: { id: 1, name: `${resourceName}-item` } };\r\n } else {\r\n // 列表\r\n response = { message: 'Success', data: [{ id: 1, name: `${resourceName}-1` }], total: 1 };\r\n }\r\n break;\r\n case 'POST':\r\n status = 201;\r\n response = { message: 'Created', data: { id: 2, name: `{{body.name}}` } };\r\n break;\r\n case 'PUT':\r\n response = { message: 'Updated', data: { id: 1, name: `{{body.name}}` } };\r\n break;\r\n case 'DELETE':\r\n status = 204;\r\n response = null;\r\n break;\r\n case 'PATCH':\r\n response = { message: 'Updated', data: { id: 1, name: `{{body.name}}` } };\r\n break;\r\n default:\r\n response = { message: 'Success' };\r\n }\r\n\r\n routes.push({\r\n method: call.method,\r\n path: call.url,\r\n status,\r\n response,\r\n delay: 100,\r\n sourceFile: call.sourceFile,\r\n });\r\n }\r\n\r\n return routes;\r\n}\r\n\r\n/** 自动生成的 Mock 路由候选项 */\r\nexport interface MockRouteCandidate {\r\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\r\n path: string;\r\n status?: number;\r\n response?: unknown;\r\n delay?: number;\r\n /** 来源文件 */\r\n sourceFile: string;\r\n}\r\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA+EN,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAmIX,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAuBL,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["path","fs","chalk","pathToFileURL","module","import_node_fs","import_node_path","import_node_fs","import_node_path","fs","path","fs","import_node_fs","import_node_path","import_node_url","path","Handlebars","fs","import_node_fs","import_node_path","path","fs","import_node_path","import_node_fs","path","os","fs","import_node_child_process","import_node_path","path","import_node_child_process","import_node_fs","import_node_path","path","fs","module","import_node_fs","import_node_path","fs","pixelmatch","path","import_node_fs","import_node_path","path","fs","exports"]}