Files

397 lines
13 KiB
JavaScript

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✅ 测试完成,浏览器已关闭');
}
})();