develop #3
@@ -0,0 +1,354 @@
|
||||
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 userMenuSelectors = [
|
||||
'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('未找到用户管理菜单');
|
||||
}
|
||||
} 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 roleMenuSelectors = [
|
||||
'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('未找到角色管理菜单');
|
||||
}
|
||||
} catch (error) {
|
||||
logTest('导航到角色管理页面', false, error.message);
|
||||
}
|
||||
|
||||
// ==================== 阶段4: 系统配置 ====================
|
||||
console.log('\n📋 阶段4: 系统配置测试');
|
||||
console.log('=====================================');
|
||||
|
||||
try {
|
||||
const configMenuSelectors = [
|
||||
'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('未找到系统配置菜单');
|
||||
}
|
||||
} 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✅ 测试完成,浏览器已关闭');
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user