/** * MCP Playwright 完整测试 * 测试所有页面并检查: * 1. 页面加载正常 * 2. 控制台无错误 * 3. UI 展示正常 * 4. 基本功能可用 */ const { chromium } = require('playwright'); const fs = require('fs'); // 测试配置 const BASE_URL = 'http://localhost:3000'; const SCREENSHOT_DIR = 'screenshots/mcp-full-test'; const TEST_USER = { username: 'testuser', password: 'Password123@' }; // 创建截图目录 if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); } // 测试结果收集 const testResults = { timestamp: new Date().toISOString(), pages: [], summary: { total: 0, passed: 0, failed: 0, errors: [] } }; /** * 测试单个页面 */ async function testPage(page, pageInfo) { const pageResult = { name: pageInfo.name, url: pageInfo.url, passed: true, errors: [], warnings: [], screenshots: [] }; console.log(`\n📄 测试页面: ${pageInfo.name}`); console.log(` URL: ${pageInfo.url}`); try { // 监听控制台消息 const consoleMessages = []; page.on('console', msg => { const type = msg.type(); const text = msg.text(); if (type === 'error') { consoleMessages.push({ type, text }); pageResult.errors.push(`[Console Error] ${text}`); } else if (type === 'warning') { pageResult.warnings.push(`[Console Warning] ${text}`); } }); // 监听页面错误 const pageErrors = []; page.on('pageerror', error => { pageErrors.push(error.toString()); pageResult.errors.push(`[Page Error] ${error.message}`); }); // 导航到页面 console.log(` ⏳ 正在加载页面...`); await page.goto(pageInfo.url, { waitUntil: 'networkidle', timeout: 30000 }); // 等待页面稳定 await page.waitForTimeout(2000); // 检查页面标题 const title = await page.title(); console.log(` 📋 页面标题: ${title}`); // 截图 - 完整页面 const fullScreenshot = `${SCREENSHOT_DIR}/${pageInfo.name}-full.png`; await page.screenshot({ path: fullScreenshot, fullPage: true }); pageResult.screenshots.push(fullScreenshot); console.log(` 📸 完整截图: ${fullScreenshot}`); // 截图 - 视口 const viewportScreenshot = `${SCREENSHOT_DIR}/${pageInfo.name}-viewport.png`; await page.screenshot({ path: viewportScreenshot, fullPage: false }); pageResult.screenshots.push(viewportScreenshot); // 检查页面基本信息 const bodyText = await page.evaluate(() => document.body.innerText); const hasContent = bodyText.length > 100; console.log(` 📝 内容长度: ${bodyText.length} 字符`); if (!hasContent) { pageResult.errors.push('页面内容过少,可能未正常加载'); pageResult.passed = false; } // 检查是否有死链 const brokenLinks = await page.evaluate(() => { const links = Array.from(document.querySelectorAll('a[href]')); return links.filter(link => { const href = link.getAttribute('href'); return href && href.startsWith('http') && !href.includes(window.location.hostname); }).length; }); if (brokenLinks > 0) { pageResult.warnings.push(`发现 ${brokenLinks} 个外部链接`); } // 检查响应式设计 const mobileViewport = { width: 375, height: 667 }; await page.setViewportSize(mobileViewport); await page.waitForTimeout(500); const mobileScreenshot = `${SCREENSHOT_DIR}/${pageInfo.name}-mobile.png`; await page.screenshot({ path: mobileScreenshot }); pageResult.screenshots.push(mobileScreenshot); console.log(` 📱 移动端截图: ${mobileScreenshot}`); // 恢复桌面视口 await page.setViewportSize({ width: 1280, height: 720 }); // 执行页面特定测试 if (pageInfo.test) { console.log(` 🔧 执行页面特定测试...`); await pageInfo.test(page, pageResult); } // 统计错误 if (pageResult.errors.length > 0) { pageResult.passed = false; console.log(` ❌ 发现 ${pageResult.errors.length} 个错误:`); pageResult.errors.forEach(err => console.log(` - ${err}`)); } if (pageResult.warnings.length > 0) { console.log(` ⚠️ 发现 ${pageResult.warnings.length} 个警告:`); pageResult.warnings.slice(0, 3).forEach(warn => console.log(` - ${warn}`)); } if (pageResult.passed) { console.log(` ✅ 页面测试通过`); } else { console.log(` ❌ 页面测试失败`); } } catch (error) { pageResult.passed = false; pageResult.errors.push(`测试异常: ${error.message}`); console.log(` ❌ 测试失败: ${error.message}`); } return pageResult; } /** * 主测试流程 */ async function runTests() { console.log('🎭 MCP Playwright 完整测试开始'); console.log('='.repeat(60)); console.log(`基础URL: ${BASE_URL}`); console.log(`截图目录: ${SCREENSHOT_DIR}`); console.log('='.repeat(60)); const browser = await chromium.launch({ headless: false, channel: 'chrome' }); const context = await browser.newContext({ viewport: { width: 1280, height: 720 } }); const page = await context.newPage(); // 定义要测试的页面 const pagesToTest = [ { name: '01-homepage', url: `${BASE_URL}/`, description: '首页/登录页' }, { name: '02-dashboard', url: `${BASE_URL}/dashboard`, description: '仪表盘' }, { name: '03-documents', url: `${BASE_URL}/documents`, description: '文档管理' }, { name: '04-todos', url: `${BASE_URL}/todos`, description: '待办事项' }, { name: '05-images', url: `${BASE_URL}/images`, description: '图片管理' } ]; // 首先尝试登录 console.log('\n🔐 尝试登录...'); try { await page.goto(`${BASE_URL}/`, { waitUntil: 'networkidle' }); await page.waitForTimeout(1000); // 查找并填写登录表单 const usernameInput = page.locator('input[type="text"]').first(); const passwordInput = page.locator('input[type="password"]').first(); const loginButton = page.locator('button').filter({ hasText: /登录|Login/i }).first(); if (await usernameInput.count() > 0 && await passwordInput.count() > 0) { await usernameInput.fill(TEST_USER.username); await passwordInput.fill(TEST_USER.password); if (await loginButton.count() > 0) { await loginButton.click(); console.log(' ✅ 登录表单已提交'); await page.waitForTimeout(2000); } else { console.log(' ⚠️ 未找到登录按钮'); } } else { console.log(' ℹ️ 未找到登录表单,可能已经登录'); } } catch (error) { console.log(` ⚠️ 登录过程异常: ${error.message}`); } // 测试每个页面 for (const pageInfo of pagesToTest) { const result = await testPage(page, pageInfo); testResults.pages.push(result); testResults.summary.total++; if (result.passed) { testResults.summary.passed++; } else { testResults.summary.failed++; testResults.summary.errors.push(...result.errors); } // 页面间等待 await page.waitForTimeout(1000); } // 生成测试报告 console.log('\n' + '='.repeat(60)); console.log('📊 测试汇总'); console.log('='.repeat(60)); testResults.pages.forEach(page => { const status = page.passed ? '✅' : '❌'; console.log(`${status} ${page.name} - ${page.errors.length} 错误, ${page.warnings.length} 警告`); }); console.log('\n总计:'); console.log(` 总页面数: ${testResults.summary.total}`); console.log(` 通过: ${testResults.summary.passed} ✅`); console.log(` 失败: ${testResults.summary.failed} ❌`); console.log(` 通过率: ${((testResults.summary.passed / testResults.summary.total) * 100).toFixed(1)}%`); if (testResults.summary.errors.length > 0) { console.log('\n⚠️ 所有错误:'); testResults.summary.errors.forEach((err, i) => { console.log(` ${i + 1}. ${err}`); }); } console.log('\n📸 所有截图保存在:', SCREENSHOT_DIR); // 保存测试结果 const resultsPath = 'test-results.json'; fs.writeFileSync(resultsPath, JSON.stringify(testResults, null, 2)); console.log('📄 测试结果已保存到:', resultsPath); // 等待用户查看 console.log('\n⏳ 5秒后关闭浏览器...'); await page.waitForTimeout(5000); await browser.close(); console.log('\n🎉 测试完成!'); return testResults; } // 运行测试 runTests() .then(results => { const exitCode = results.summary.failed > 0 ? 1 : 0; process.exit(exitCode); }) .catch(error => { console.error('❌ 测试运行失败:', error); process.exit(1); });