UNPKG

vue3-test-config

Version:
949 lines (811 loc) 28.2 kB
#!/usr/bin/env node /* * @Author: wangchao67 wangchao67@mychery.com * @Date: 2025-06-09 14:08:14 * @LastEditors: wangchao67 * @LastEditTime: 2025-06-09 18:51:55 * @Description: file content */ import fs from "fs"; import path from "path"; import { execSync } from "child_process"; import readline from "readline"; import { fileURLToPath } from "url"; // 获取当前文件的目录路径(ES模块中没有__dirname) const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); class TestSetup { constructor() { // 支持指定项目路径或使用当前工作目录 this.projectRoot = process.argv[2] || process.cwd(); this.packageJsonPath = path.join(this.projectRoot, "package.json"); // 检查项目目录是否存在 if (!fs.existsSync(this.projectRoot)) { console.error(`❌ 项目目录不存在: ${this.projectRoot}`); process.exit(1); } // 检查是否为有效的Vue项目 if (!fs.existsSync(this.packageJsonPath)) { console.error(`❌ 未找到package.json文件: ${this.packageJsonPath}`); process.exit(1); } this.packageJson = this.readPackageJson(); this.isTypeScript = this.detectTypeScript(); this.scriptLanguage = null; // 用户选择的脚本语言 this.supportBoth = false; // 是否支持两种语言 console.log(`📍 项目路径: ${this.projectRoot}`); } // 创建readline接口 createReadlineInterface() { return readline.createInterface({ input: process.stdin, output: process.stdout, }); } // 询问用户选择脚本语言 async askScriptLanguage() { const rl = this.createReadlineInterface(); console.log("\n🤔 请选择测试脚本的语言支持:"); console.log("1. JavaScript 脚本"); console.log("2. TypeScript 脚本"); console.log("3. 同时支持 JavaScript 和 TypeScript"); const answer = await new Promise((resolve) => { rl.question("\n请选择 (1/2/3): ", (answer) => { resolve(answer.trim()); }); }); rl.close(); switch (answer) { case "1": this.scriptLanguage = "js"; this.supportBoth = false; console.log("✅ 选择:JavaScript 脚本"); break; case "2": this.scriptLanguage = "ts"; this.supportBoth = false; console.log("✅ 选择:TypeScript 脚本"); break; case "3": this.scriptLanguage = "both"; this.supportBoth = true; console.log("✅ 选择:同时支持 JavaScript 和 TypeScript"); break; default: console.log("⚠️ 无效选择,默认使用项目检测结果"); this.scriptLanguage = this.isTypeScript ? "ts" : "js"; this.supportBoth = false; } } // 获取文件扩展名数组 getFileExtensions() { if (this.supportBoth) { return ["js", "ts"]; } return [this.scriptLanguage || (this.isTypeScript ? "ts" : "js")]; } // 读取package.json readPackageJson() { try { const content = fs.readFileSync(this.packageJsonPath, "utf8"); return JSON.parse(content); } catch (error) { console.error("❌ 无法读取package.json文件:", error.message); process.exit(1); } } // 检测是否使用TypeScript detectTypeScript() { const hasTypeScript = this.packageJson.dependencies?.typescript || this.packageJson.devDependencies?.typescript || fs.existsSync(path.join(this.projectRoot, "tsconfig.json")); console.log( `🔍 检测到项目使用: ${hasTypeScript ? "TypeScript" : "JavaScript"}` ); return hasTypeScript; } // 检查包是否已安装 isPackageInstalled(packageName) { return !!( this.packageJson.dependencies?.[packageName] || this.packageJson.devDependencies?.[packageName] ); } // 检查vitest版本冲突,避免parseAstAsync错误 checkVitestVersionConflicts() { const vitestVersion = this.packageJson.devDependencies?.vitest || this.packageJson.dependencies?.vitest; if (vitestVersion) { console.log(`🔍 当前vitest版本: ${vitestVersion}`); if (vitestVersion.includes("1.") || vitestVersion.includes("2.")) { console.log("⚠️ 检测到旧版本vitest,可能导致parseAstAsync错误"); } } } // 检查版本冲突的包 checkForVersionConflicts(targetPackages) { const conflictingPackages = []; targetPackages.forEach((pkg) => { const [packageName, targetVersion] = pkg.split("@"); const actualPkgName = packageName === "" ? "@" + pkg.split("@")[1] : packageName; const installedVersion = this.packageJson.devDependencies?.[actualPkgName] || this.packageJson.dependencies?.[actualPkgName]; if (installedVersion) { // 检查是否是低版本的vitest相关包 if ( actualPkgName.includes("vitest") && (installedVersion.includes("1.") || installedVersion.includes("2.")) ) { conflictingPackages.push(actualPkgName); } } }); return conflictingPackages; } // 卸载冲突的包 uninstallConflictingPackages(packages) { try { const packageList = packages.join(" "); console.log(`🗑️ 正在卸载冲突包: ${packageList}`); execSync(`npm uninstall ${packageList}`, { stdio: "inherit", cwd: this.projectRoot, }); console.log(`✅ 卸载完成: ${packageList}`); // 重新读取package.json this.packageJson = this.readPackageJson(); } catch (error) { console.error(`❌ 卸载失败: ${packages}`, error.message); throw error; } } // 安装包 installPackage(packages, isDev = true) { const devFlag = isDev ? "--save-dev" : "--save"; const packageList = Array.isArray(packages) ? packages.join(" ") : packages; try { console.log(`📦 正在安装: ${packageList}`); // 确保在正确的项目目录中执行npm命令 execSync(`npm install ${devFlag} ${packageList}`, { stdio: "inherit", cwd: this.projectRoot, }); console.log(`✅ 安装完成: ${packageList}`); } catch (error) { console.error(`❌ 安装失败: ${packageList}`, error.message); throw error; } } // 创建目录 createDirectory(dirPath) { const fullPath = path.isAbsolute(dirPath) ? dirPath : path.join(this.projectRoot, dirPath); if (!fs.existsSync(fullPath)) { fs.mkdirSync(fullPath, { recursive: true }); console.log(`📁 创建目录: ${path.relative(this.projectRoot, fullPath)}`); return true; } else { console.log( `📁 目录已存在: ${path.relative(this.projectRoot, fullPath)}` ); return false; } } // 创建文件 createFile(filePath, content, overwrite = false) { const fullPath = path.isAbsolute(filePath) ? filePath : path.join(this.projectRoot, filePath); if (!fs.existsSync(fullPath) || overwrite) { fs.writeFileSync(fullPath, content); console.log(`📄 创建文件: ${path.relative(this.projectRoot, fullPath)}`); return true; } else { console.log( `📄 文件已存在: ${path.relative(this.projectRoot, fullPath)}` ); return false; } } // 配置Vitest setupVitest() { console.log("\n🧪 配置Vitest..."); // 检查现有vitest版本,避免parseAstAsync错误 this.checkVitestVersionConflicts(); // 检查并安装vitest - 使用3.2.2版本 const vitestPackages = [ "vitest@^3.2.2", "@vue/test-utils@^2.4.0", "happy-dom@^12.0.0", "@vitest/coverage-v8@^3.2.2", "@vitest/ui@^3.2.2", ]; const missingPackages = vitestPackages.filter((pkg) => { // 提取包名(去掉版本号)进行检查 const packageName = pkg.split("@")[0] === "" ? "@" + pkg.split("@")[1] : pkg.split("@")[0]; return !this.isPackageInstalled(packageName); }); // 检查是否存在版本冲突的包 const conflictingPackages = this.checkForVersionConflicts(vitestPackages); if (conflictingPackages.length > 0) { console.log("⚠️ 检测到版本冲突的包,需要先卸载:"); conflictingPackages.forEach((pkg) => console.log(` - ${pkg}`)); this.uninstallConflictingPackages(conflictingPackages); } if (missingPackages.length > 0 || conflictingPackages.length > 0) { console.log( "📋 将安装以下Vitest相关包(3.2.2版本,避免parseAstAsync错误):" ); vitestPackages.forEach((pkg) => console.log(` - ${pkg}`)); this.installPackage(vitestPackages); // 重新读取package.json以获取最新的依赖信息 this.packageJson = this.readPackageJson(); } else { console.log("✅ Vitest相关包已安装且版本兼容"); } // 创建vitest配置文件 const extensions = this.getFileExtensions(); extensions.forEach((ext) => { const vitestConfig = ext === "ts" ? this.getVitestConfigTS() : this.getVitestConfigJS(); const configFileName = `vitest.config.${ext}`; this.createFile(configFileName, vitestConfig); }); // 验证安装是否成功,预防parseAstAsync错误 this.verifyVitestInstallation(); } // 验证vitest安装是否成功 verifyVitestInstallation() { try { console.log("\n🔍 验证Vitest安装..."); // 检查关键包的版本 const packageJson = this.readPackageJson(); const vitestVersion = packageJson.devDependencies?.vitest || packageJson.dependencies?.vitest; const coverageVersion = packageJson.devDependencies?.["@vitest/coverage-v8"] || packageJson.dependencies?.["@vitest/coverage-v8"]; const uiVersion = packageJson.devDependencies?.["@vitest/ui"] || packageJson.dependencies?.["@vitest/ui"]; if (vitestVersion && vitestVersion.includes("3.2")) { console.log("✅ Vitest 3.2.x 版本安装成功,已避免parseAstAsync错误"); console.log(` vitest: ${vitestVersion}`); if (coverageVersion) console.log(` @vitest/coverage-v8: ${coverageVersion}`); if (uiVersion) console.log(` @vitest/ui: ${uiVersion}`); } else { console.log("⚠️ 警告:Vitest版本可能不兼容,建议手动检查"); } } catch (error) { console.log("⚠️ 验证过程中出现问题,请手动检查安装结果"); } } // 配置Cypress setupCypress() { console.log("\n🚀 配置Cypress..."); // 检查并安装cypress if (!this.isPackageInstalled("cypress")) { this.installPackage("cypress"); // 重新读取package.json this.packageJson = this.readPackageJson(); } else { console.log("✅ Cypress已安装"); } // 创建cypress配置文件 const extensions = this.getFileExtensions(); extensions.forEach((ext) => { const cypressConfig = this.getCypressConfig(ext); const configFileName = `cypress.config.${ext}`; this.createFile(configFileName, cypressConfig); }); // 创建cypress支持文件目录 - 修改为test/e2e/support const cypressSupportDir = "test/e2e/support"; this.createDirectory(cypressSupportDir); // 为每种语言创建支持文件 extensions.forEach((ext) => { const supportFile = ext === "ts" ? this.getCypressSupportTS() : this.getCypressSupportJS(); const supportFileName = `e2e.${ext}`; this.createFile( path.join(cypressSupportDir, supportFileName), supportFile ); // 创建cypress commands文件 const commandsFile = this.getCypressCommands(); const commandsFileName = `commands.${ext}`; this.createFile( path.join(cypressSupportDir, commandsFileName), commandsFile ); }); } // 创建目录结构 createDirectoryStructure() { console.log("\n📁 创建测试目录结构..."); this.createDirectory("test"); this.createDirectory("test/unit"); this.createDirectory("test/e2e"); this.createDirectory("test/e2e/support"); // 添加support目录 } // 更新package.json脚本 updatePackageJsonScripts() { console.log("\n📝 更新package.json脚本..."); const scripts = this.packageJson.scripts || {}; const newScripts = { test: "vitest", "test:ui": "vitest --ui", "test:run": "vitest run", "test:coverage": "vitest run --coverage", "cypress:open": "cypress open", "cypress:run": "cypress run", "test:e2e": "cypress run", "test:e2e:open": "cypress open", }; let hasChanges = false; Object.entries(newScripts).forEach(([key, value]) => { if (!scripts[key]) { scripts[key] = value; hasChanges = true; console.log(`✅ 添加脚本: ${key}`); } else { console.log(`📄 脚本已存在: ${key}`); } }); if (hasChanges) { this.packageJson.scripts = scripts; fs.writeFileSync( this.packageJsonPath, JSON.stringify(this.packageJson, null, 2) ); console.log("✅ package.json脚本更新完成"); } } // 创建示例测试文件 createDemoTests() { console.log("\n📋 创建示例测试文件..."); const extensions = this.getFileExtensions(); extensions.forEach((ext) => { // 创建单元测试示例 const unitTestContent = ext === "ts" ? this.getUnitTestDemoTS() : this.getUnitTestDemoJS(); const unitTestPath = `test/unit/example.test.${ext}`; this.createFile(unitTestPath, unitTestContent); // 创建e2e测试示例 const e2eTestContent = ext === "ts" ? this.getE2ETestDemoTS() : this.getE2ETestDemoJS(); const e2eTestPath = `test/e2e/example.cy.${ext}`; this.createFile(e2eTestPath, e2eTestContent); }); } // 获取Vitest配置内容 getVitestConfigTS() { // 始终支持.js和.ts文件,因为项目中可能混合使用 const includePattern = "test/unit/**/*.test.{js,ts}"; return `import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' import { fileURLToPath, URL } from 'node:url' export default defineConfig({ plugins: [vue()], test: { globals: true, environment: 'jsdom', include: ['${includePattern}'], coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], exclude: [ 'node_modules/', 'test/', 'cypress/', '**/*.d.ts' ] } }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } }) `; } getVitestConfigJS() { // 始终支持.js和.ts文件,因为项目中可能混合使用 const includePattern = "test/unit/**/*.test.{js,ts}"; return `import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' import { fileURLToPath, URL } from 'node:url' export default defineConfig({ plugins: [vue()], test: { globals: true, environment: 'jsdom', include: ['${includePattern}'], coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], exclude: [ 'node_modules/', 'test/', 'cypress/', '**/*.d.ts' ] } }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } }) `; } // 获取Cypress配置内容 - 修复:统一使用ES模块语法 getCypressConfig(extension) { const specPattern = this.supportBoth ? "test/e2e/**/*.cy.{js,ts}" : `test/e2e/**/*.cy.${extension}`; const supportFile = `test/e2e/support/e2e.${extension}`; // 统一使用ES模块语法,适配Vue3+Vite项目 const config = `import { defineConfig } from 'cypress' export default defineConfig({ e2e: { baseUrl: 'http://localhost:5173', specPattern: '${specPattern}', supportFile: '${supportFile}', video: true, screenshotOnRunFailure: true, viewportWidth: 1280, viewportHeight: 720, defaultCommandTimeout: 8000, setupNodeEvents(on, config) { // 这里可以添加插件配置 }, }, }) `; return config; } // 获取Cypress支持文件内容 - 确保使用ES模块语法 getCypressSupportTS() { return `// Cypress支持文件 import './commands' // 全局配置 Cypress.on('uncaught:exception', (err, runnable) => { // 返回false来阻止Cypress失败测试 console.warn('Uncaught exception:', err.message) return false }) // 在每个测试前的全局配置 beforeEach(() => { // 可以在这里添加全局的前置操作 }) `; } getCypressSupportJS() { return `// Cypress支持文件 import './commands' // 全局配置 Cypress.on('uncaught:exception', (err, runnable) => { // 返回false来阻止Cypress失败测试 console.warn('Uncaught exception:', err.message) return false }) // 在每个测试前的全局配置 beforeEach(() => { // 可以在这里添加全局的前置操作 }) `; } // 获取Cypress commands文件内容 getCypressCommands() { return `// Cypress自定义命令 // 您可以在这里添加自定义的Cypress命令 // 示例:添加一个登录命令 // Cypress.Commands.add('login', (username, password) => { // cy.visit('/login') // cy.get('[data-testid="username"]').type(username) // cy.get('[data-testid="password"]').type(password) // cy.get('[data-testid="login-button"]').click() // }) // 示例:检查元素是否可见 Cypress.Commands.add('checkVisible', (selector) => { cy.get(selector).should('be.visible') }) // 示例:等待元素出现 Cypress.Commands.add('waitForElement', (selector, timeout = 10000) => { cy.get(selector, { timeout }).should('exist') }) `; } // 获取单元测试示例 getUnitTestDemoTS() { return `import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' // 示例测试:测试一个简单的Vue组件 describe('示例单元测试 (TypeScript)', () => { it('应该进行基础的数学运算', () => { expect(1 + 1).toBe(2) }) it('应该测试字符串操作', () => { const message: string = 'Hello Vue3' expect(message).toContain('Vue3') expect(message.length).toBe(10) }) it('应该测试数组操作', () => { const list: string[] = ['vue3', 'ant-design-vue', 'pinia', 'vue-router', 'vite'] expect(list).toHaveLength(5) expect(list).toContain('vue3') }) it('应该测试对象操作', () => { interface User { name: string age: number skills: string[] } const user: User = { name: '张三', age: 25, skills: ['Vue3', 'TypeScript'] } expect(user).toHaveProperty('name', '张三') expect(user.skills).toContain('Vue3') }) it('应该测试异步操作', async () => { const promise = Promise.resolve('success') await expect(promise).resolves.toBe('success') }) }) // Vue组件测试示例(需要实际组件时启用) /* import YourComponent from '@/components/YourComponent.vue' describe('Vue组件测试', () => { it('应该渲染组件', () => { const wrapper = mount(YourComponent, { props: { // 组件props } }) expect(wrapper.exists()).toBe(true) expect(wrapper.find('[data-testid="component"]').exists()).toBe(true) }) it('应该响应用户交互', async () => { const wrapper = mount(YourComponent) await wrapper.find('[data-testid="button"]').trigger('click') expect(wrapper.emitted()).toHaveProperty('click') }) }) */ `; } getUnitTestDemoJS() { return `import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' // 示例测试:基础功能测试 describe('示例单元测试 (JavaScript)', () => { it('应该进行基础的数学运算', () => { expect(1 + 1).toBe(2) }) it('应该测试字符串操作', () => { const message = 'Hello Vue3' expect(message).toContain('Vue3') expect(message.length).toBe(10) }) it('应该测试数组操作', () => { const list = ['vue3', 'ant-design-vue', 'pinia', 'vue-router', 'vite'] expect(list).toHaveLength(5) expect(list).toContain('vue3') }) it('应该测试对象操作', () => { const user = { name: '张三', age: 25, skills: ['Vue3', 'JavaScript'] } expect(user).toHaveProperty('name', '张三') expect(user.skills).toContain('Vue3') }) it('应该测试异步操作', async () => { const promise = Promise.resolve('success') await expect(promise).resolves.toBe('success') }) }) // Vue组件测试示例(需要实际组件时启用) /* import YourComponent from '@/components/YourComponent.vue' describe('Vue组件测试', () => { it('应该渲染组件', () => { const wrapper = mount(YourComponent, { props: { // 组件props } }) expect(wrapper.exists()).toBe(true) expect(wrapper.find('[data-testid="component"]').exists()).toBe(true) }) it('应该响应用户交互', async () => { const wrapper = mount(YourComponent) await wrapper.find('[data-testid="button"]').trigger('click') expect(wrapper.emitted()).toHaveProperty('click') }) }) */ `; } // 获取E2E测试示例 getE2ETestDemoTS() { return `describe('vueDemo项目E2E测试 (TypeScript)', () => { beforeEach(() => { // 访问应用首页 cy.visit('/') }) it('应该显示页面标题', () => { cy.title().should('not.be.empty') }) it('应该包含基本页面元素', () => { // 检查页面是否包含某些基本元素 cy.get('body').should('be.visible') }) it('应该能够进行基本的页面交互', () => { // 示例:点击操作(根据您的实际页面调整选择器) // cy.get('[data-testid="button"]').click() // 示例:输入操作 // cy.get('[data-testid="input"]').type('测试文本') // 示例:检查URL变化 // cy.url().should('include', '/expected-path') // 当前示例:检查页面加载 cy.get('body').should('exist') }) it('应该测试页面导航', () => { // 示例:测试路由导航 // cy.get('[data-testid="nav-about"]').click() // cy.url().should('include', '/about') // 当前示例:检查页面标题 cy.get('head title').should('exist') }) it('应该测试响应式设计', () => { // 测试不同视口尺寸 cy.viewport(375, 667) // iPhone 6/7/8 cy.get('body').should('be.visible') cy.viewport(1280, 720) // Desktop cy.get('body').should('be.visible') }) it('应该测试页面性能', () => { // 检查页面加载时间 cy.window().its('performance').invoke('now').should('be.lessThan', 5000) }) }) // 注意:这是一个基础示例 // 实际使用时,请根据您的应用页面结构调整测试选择器和操作 `; } getE2ETestDemoJS() { return `describe('vueDemo项目E2E测试 (JavaScript)', () => { beforeEach(() => { // 访问应用首页 cy.visit('/') }) it('应该显示页面标题', () => { cy.title().should('not.be.empty') }) it('应该包含基本页面元素', () => { // 检查页面是否包含某些基本元素 cy.get('body').should('be.visible') }) it('应该能够进行基本的页面交互', () => { // 示例:点击操作(根据您的实际页面调整选择器) // cy.get('[data-testid="button"]').click() // 示例:输入操作 // cy.get('[data-testid="input"]').type('测试文本') // 示例:检查URL变化 // cy.url().should('include', '/expected-path') // 当前示例:检查页面加载 cy.get('body').should('exist') }) it('应该测试页面导航', () => { // 示例:测试路由导航 // cy.get('[data-testid="nav-about"]').click() // cy.url().should('include', '/about') // 当前示例:检查页面标题 cy.get('head title').should('exist') }) it('应该测试响应式设计', () => { // 测试不同视口尺寸 cy.viewport(375, 667) // iPhone 6/7/8 cy.get('body').should('be.visible') cy.viewport(1280, 720) // Desktop cy.get('body').should('be.visible') }) it('应该测试页面性能', () => { // 检查页面加载时间 cy.window().its('performance').invoke('now').should('be.lessThan', 5000) }) }) // 注意:这是一个基础示例 // 实际使用时,请根据您的应用页面结构调整测试选择器和操作 `; } // 主要执行方法 async run() { console.log("🚀 开始配置测试环境...\n"); console.log(`📁 目标项目: ${this.projectRoot}\n`); try { // 询问用户选择脚本语言 await this.askScriptLanguage(); // 检查是否为Vue项目 const isVueProject = this.packageJson.dependencies?.vue || this.packageJson.devDependencies?.vue || this.packageJson.dependencies?.["@vitejs/plugin-vue"]; if (!isVueProject) { console.warn("⚠️ 警告:这似乎不是一个Vue项目,但会继续配置测试环境"); } // 1. 配置Vitest this.setupVitest(); // 2. 配置Cypress this.setupCypress(); // 3. 创建目录结构 this.createDirectoryStructure(); // 4. 更新package.json脚本 this.updatePackageJsonScripts(); // 5. 创建示例测试文件 this.createDemoTests(); console.log("\n✅ 测试环境配置完成!"); // 显示生成的文件 console.log("\n📄 生成的配置文件:"); const extensions = this.getFileExtensions(); extensions.forEach((ext) => { console.log(` - vitest.config.${ext}`); console.log(` - cypress.config.${ext}`); }); console.log("\n📋 可用的测试命令:"); console.log(" npm run test # 运行单元测试(监听模式)"); console.log(" npm run test:ui # 打开Vitest UI界面"); console.log(" npm run test:run # 运行单元测试(单次)"); console.log(" npm run test:coverage # 运行单元测试并生成覆盖率报告"); console.log(" npm run cypress:open # 打开Cypress测试界面"); console.log(" npm run cypress:run # 运行E2E测试(无头模式)"); console.log(" npm run test:e2e # 运行E2E测试"); console.log(" npm run test:e2e:open # 打开E2E测试界面"); console.log("\n📁 目录结构:"); console.log(" test/"); console.log(" ├── unit/ # 单元测试文件"); console.log(" └── e2e/ # E2E测试文件"); console.log(" ├── support/ # Cypress支持文件"); if (this.supportBoth) { console.log(" ├── *.cy.js # E2E测试用例(JS)"); console.log(" └── *.cy.ts # E2E测试用例(TS)"); } else { console.log( ` └── *.cy.${this.scriptLanguage} # E2E测试用例` ); } console.log("\n💡 使用提示:"); console.log(" - 确保您的开发服务器运行在 http://localhost:5173"); console.log(" - 可以根据需要调整配置文件中的设置"); console.log(" - 已自动安装 @vue/test-utils 用于Vue组件测试"); console.log(" - 使用 data-testid 属性来为测试选择元素"); console.log(" - 运行 'npm run dev' 启动开发服务器后再进行E2E测试"); console.log(" - 所有测试文件统一放在 test/ 目录下"); if (this.supportBoth) { console.log(" - 支持 JavaScript 和 TypeScript 两种测试脚本"); } else { console.log(` - 使用 ${this.scriptLanguage.toUpperCase()} 测试脚本`); } } catch (error) { console.error("\n❌ 配置过程中发生错误:", error.message); console.error("堆栈信息:", error.stack); process.exit(1); } } } // 执行配置 const setup = new TestSetup(); setup.run();