import { chromium } from 'playwright'; import { writeFileSync } from 'fs'; // 配置参数 const TARGET_URL = process.env.TARGET_URL || 'http://localhost:3002'; const API_URL = process.env.API_URL || 'http://localhost:8080'; const TEST_USER = { username: 'admin', password: 'admin123' }; // 测试结果收集 const testResults = { total: 0, passed: 0, failed: 0, errors: [] }; // 辅助函数:记录测试结果 function logTest(testName, passed, error = null) { testResults.total++; if (passed) { testResults.passed++; console.log(`✅ ${testName}`); } else { testResults.failed++; testResults.errors.push({ testName, error }); console.log(`❌ ${testName}: ${error}`); } } // 辅助函数:等待并截图 async function captureStep(page, stepName) { const screenshotPath = `/tmp/user-journey-${stepName}.png`; await page.screenshot({ path: screenshotPath, fullPage: true }); console.log(`📸 Screenshot saved: ${screenshotPath}`); } (async () => { console.log('🚀 开始User Journey测试...'); console.log(`📍 目标URL: ${TARGET_URL}`); console.log(`📍 API URL: ${API_URL}`); console.log(''); const browser = await chromium.launch({ headless: false, slowMo: 100 }); const context = await browser.newContext({ viewport: { width: 1920, height: 1080 }, locale: 'zh-CN' }); const page = await context.newPage(); try { // ==================== 阶段1: 登录流程 ==================== console.log('📋 阶段1: 登录流程测试'); console.log('====================================='); // 1.1 访问登录页面 try { await page.goto(`${TARGET_URL}/login`, { waitUntil: 'networkidle' }); await captureStep(page, '01-login-page'); logTest('访问登录页面', true); } catch (error) { logTest('访问登录页面', false, error.message); } // 1.2 验证登录页面元素 try { const usernameInput = page.locator('input[type="text"], input[placeholder*="用户名"], input[placeholder*="账号"]').first(); const passwordInput = page.locator('input[type="password"]').first(); const loginButton = page.locator('button:has-text("登录")').first(); await usernameInput.waitFor({ state: 'visible', timeout: 5000 }); await passwordInput.waitFor({ state: 'visible', timeout: 5000 }); await loginButton.waitFor({ state: 'visible', timeout: 5000 }); logTest('登录页面元素验证', true); } catch (error) { logTest('登录页面元素验证', false, error.message); } // 1.3 填写登录表单 try { const usernameInput = page.locator('input[type="text"], input[placeholder*="用户名"], input[placeholder*="账号"]').first(); const passwordInput = page.locator('input[type="password"]').first(); await usernameInput.fill(TEST_USER.username); await passwordInput.fill(TEST_USER.password); await captureStep(page, '02-login-form-filled'); logTest('填写登录表单', true); } catch (error) { logTest('填写登录表单', false, error.message); } // 1.4 提交登录 try { const loginButton = page.locator('button:has-text("登录")').first(); // 监听登录响应 const responsePromise = page.waitForResponse(response => response.url().includes('/api/auth/login') && response.request().method() === 'POST', { timeout: 10000 } ); await loginButton.click(); const response = await responsePromise; const loginData = await response.json(); if (response.status() === 200 && loginData.token) { console.log(`🔑 登录成功,Token: ${loginData.token.substring(0, 20)}...`); logTest('提交登录表单', true); } else { throw new Error(`登录失败: ${JSON.stringify(loginData)}`); } } catch (error) { logTest('提交登录表单', false, error.message); } // 1.5 等待页面跳转 try { await page.waitForTimeout(2000); const currentUrl = page.url(); if (currentUrl.includes('dashboard') || currentUrl.includes('home') || !currentUrl.includes('login')) { await captureStep(page, '03-after-login'); logTest('登录后页面跳转', true); } else { throw new Error(`未跳转到主页,当前URL: ${currentUrl}`); } } catch (error) { logTest('登录后页面跳转', false, error.message); } // ==================== 阶段2: 用户管理 ==================== console.log('\n📋 阶段2: 用户管理测试'); console.log('====================================='); // 2.1 导航到用户管理页面 try { // 首先展开系统管理菜单(如果是折叠状态) const systemMenuSelector = '.el-sub-menu:has-text("系统管理")'; const systemMenuElement = page.locator(systemMenuSelector).first(); if (await systemMenuElement.count() > 0) { // 点击展开系统管理菜单 await systemMenuElement.click(); await page.waitForTimeout(500); // 然后点击用户管理菜单项 const userMenuSelectors = [ '.el-menu-item:has-text("用户管理")', 'text=用户管理', 'text=用户', '[data-menu="user"]', 'a[href*="user"]' ]; let navigated = false; for (const selector of userMenuSelectors) { const element = page.locator(selector).first(); if (await element.count() > 0) { await element.click(); navigated = true; break; } } if (navigated) { await page.waitForTimeout(1000); await captureStep(page, '04-user-management'); logTest('导航到用户管理页面', true); } else { throw new Error('未找到用户管理菜单'); } } else { throw new Error('未找到系统管理菜单'); } } catch (error) { logTest('导航到用户管理页面', false, error.message); } // 2.2 验证用户列表 try { // 检查是否有用户列表或表格 const tableSelectors = [ 'table', '.el-table', '[class*="table"]', '.user-list' ]; let foundTable = false; for (const selector of tableSelectors) { const count = await page.locator(selector).count(); if (count > 0) { foundTable = true; break; } } if (foundTable) { logTest('用户列表显示', true); } else { throw new Error('未找到用户列表表格'); } } catch (error) { logTest('用户列表显示', false, error.message); } // ==================== 阶段3: 角色管理 ==================== console.log('\n📋 阶段3: 角色管理测试'); console.log('====================================='); try { // 首先展开系统管理菜单(如果是折叠状态) const systemMenuSelector = '.el-sub-menu:has-text("系统管理")'; const systemMenuElement = page.locator(systemMenuSelector).first(); if (await systemMenuElement.count() > 0) { // 点击展开系统管理菜单 await systemMenuElement.click(); await page.waitForTimeout(500); // 然后点击角色管理菜单项 const roleMenuSelectors = [ '.el-menu-item:has-text("角色管理")', 'text=角色管理', 'text=角色', '[data-menu="role"]', 'a[href*="role"]' ]; let navigated = false; for (const selector of roleMenuSelectors) { const element = page.locator(selector).first(); if (await element.count() > 0) { await element.click(); navigated = true; break; } } if (navigated) { await page.waitForTimeout(1000); await captureStep(page, '05-role-management'); logTest('导航到角色管理页面', true); } else { throw new Error('未找到角色管理菜单'); } } else { throw new Error('未找到系统管理菜单'); } } catch (error) { logTest('导航到角色管理页面', false, error.message); } // ==================== 阶段4: 系统配置 ==================== console.log('\n📋 阶段4: 系统配置测试'); console.log('====================================='); try { // 首先展开系统管理菜单(如果是折叠状态) const systemMenuSelector = '.el-sub-menu:has-text("系统管理")'; const systemMenuElement = page.locator(systemMenuSelector).first(); if (await systemMenuElement.count() > 0) { // 点击展开系统管理菜单 await systemMenuElement.click(); await page.waitForTimeout(500); // 然后点击参数配置菜单项 const configMenuSelectors = [ '.el-menu-item:has-text("参数配置")', '.el-menu-item:has-text("系统配置")', '.el-menu-item:has-text("配置管理")', 'text=参数配置', 'text=系统配置', 'text=配置管理', '[data-menu="config"]', 'a[href*="config"]' ]; let navigated = false; for (const selector of configMenuSelectors) { const element = page.locator(selector).first(); if (await element.count() > 0) { await element.click(); navigated = true; break; } } if (navigated) { await page.waitForTimeout(1000); await captureStep(page, '06-system-config'); logTest('导航到系统配置页面', true); } else { throw new Error('未找到系统配置菜单'); } } else { throw new Error('未找到系统管理菜单'); } } catch (error) { logTest('导航到系统配置页面', false, error.message); } // ==================== 阶段5: 登出流程 ==================== console.log('\n📋 阶段5: 登出流程测试'); console.log('====================================='); try { // 首先点击用户头像以展开下拉菜单 const avatarSelector = '.el-avatar'; const avatarElement = page.locator(avatarSelector).first(); if (await avatarElement.count() > 0) { await avatarElement.click(); await page.waitForTimeout(500); // 等待下拉菜单展开 // 然后点击退出登录按钮 const logoutSelectors = [ '.el-dropdown-menu__item:has-text("退出登录")', '.el-dropdown-menu__item:has-text("退出")', '.el-dropdown-menu__item:has-text("登出")', 'button:has-text("退出")', 'button:has-text("登出")', 'a:has-text("退出")', 'a:has-text("登出")', '[data-action="logout"]', '.logout-button' ]; let loggedOut = false; for (const selector of logoutSelectors) { const element = page.locator(selector).first(); if (await element.count() > 0) { await element.click(); loggedOut = true; break; } } if (loggedOut) { await page.waitForTimeout(2000); const currentUrl = page.url(); if (currentUrl.includes('login')) { await captureStep(page, '07-after-logout'); logTest('登出成功', true); } else { throw new Error(`登出后未跳转到登录页,当前URL: ${currentUrl}`); } } else { throw new Error('未找到登出按钮'); } } else { throw new Error('未找到用户头像'); } } catch (error) { logTest('登出成功', false, error.message); } // ==================== 测试总结 ==================== console.log('\n📊 测试总结'); console.log('====================================='); console.log(`总测试数: ${testResults.total}`); console.log(`通过: ${testResults.passed}`); console.log(`失败: ${testResults.failed}`); console.log(`通过率: ${((testResults.passed / testResults.total) * 100).toFixed(2)}%`); if (testResults.failed > 0) { console.log('\n❌ 失败的测试:'); testResults.errors.forEach((error, index) => { console.log(`${index + 1}. ${error.testName}: ${error.error}`); }); } // 保存测试报告 const reportPath = '/tmp/user-journey-report.json'; writeFileSync(reportPath, JSON.stringify(testResults, null, 2)); console.log(`\n📄 测试报告已保存: ${reportPath}`); } catch (error) { console.error('❌ 测试执行出错:', error); await captureStep(page, 'error-state'); } finally { await browser.close(); console.log('\n✅ 测试完成,浏览器已关闭'); } })();