const { chromium } = require('playwright'); const fs = require('fs'); const path = require('path'); // 所有俄罗斯方块版本 const ALL_VERSIONS = [ // Canvas 2D 版本 { file: 'tetris_final_canvas2d.html', name: '最终Canvas 2D版', type: 'canvas2d', priority: 1 }, { file: 'tetris_simple_fall_test.html', name: '简单下落测试版', type: 'canvas2d', priority: 2 }, { file: 'tetris_simple_fixed.html', name: '简单修复版', type: 'canvas2d', priority: 3 }, { file: 'tetris_working.html', name: '工作版', type: 'canvas2d', priority: 4 }, { file: 'tetris_mobile.html', name: '手机优化版', type: 'canvas2d', priority: 5 }, { file: 'tetris_ultimate.html', name: '终极版', type: 'canvas2d', priority: 6 }, { file: 'tetris_simple_canvas.html', name: 'Canvas 2D简化版', type: 'canvas2d', priority: 7 }, // PlayCanvas 版本 { file: 'tetris_playcanvas_working.html', name: 'PlayCanvas工作版', type: 'playcanvas', priority: 1 }, { file: 'tetris_playcanvas_super_debug.html', name: 'PlayCanvas超级调试版', type: 'playcanvas', priority: 2 }, { file: 'tetris_playcanvas_render_fixed.html', name: 'PlayCanvas渲染修复版', type: 'playcanvas', priority: 3 }, { file: 'tetris_playcanvas_simple.html', name: 'PlayCanvas简化版', type: 'playcanvas', priority: 4 }, { file: 'tetris_playcanvas_fixed.html', name: 'PlayCanvas修复版', type: 'playcanvas', priority: 5 }, { file: 'tetris_pure_playcanvas.html', name: '纯PlayCanvas版', type: 'playcanvas', priority: 6 }, // 其他版本 { file: 'tetris_final.html', name: '最终版', type: 'mixed', priority: 1 }, { file: 'tetris_exact_snake_copy.html', name: '贪吃蛇精确复制版', type: 'mixed', priority: 2 }, { file: 'tetris_snake_style.html', name: '贪吃蛇风格版', type: 'mixed', priority: 3 }, { file: 'tetris_no_poki.html', name: '无Poki SDK版', type: 'mixed', priority: 4 }, // 默认版本 { file: 'index.html', name: '默认版', type: 'default', priority: 1 }, ]; const BASE_URL = 'http://129.211.173.87:8895/games/tetris/'; const OUTPUT_DIR = '/tmp/tetris_auto_test'; // 创建输出目录 if (!fs.existsSync(OUTPUT_DIR)) { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); } async function testVersion(browser, version, index, total) { console.log(`\n[${index}/${total}] 🎮 测试: ${version.name}`); console.log('='.repeat(70)); const page = await browser.newPage(); const logs = []; const errors = []; const warnings = []; try { // 设置页面事件监听 page.on('console', msg => { const text = msg.text(); const type = msg.type(); logs.push({ time: new Date().toISOString(), type: type, text: text }); if (type === 'error') { errors.push(text); } else if (type === 'warning') { warnings.push(text); } }); page.on('pageerror', error => { errors.push(`页面错误: ${error.message}`); }); // 导航到页面 const url = BASE_URL + version.file; console.log(`📡 URL: ${url}`); const startTime = Date.now(); await page.goto(url, { waitUntil: 'networkidle', timeout: 15000 }); const loadTime = Date.now() - startTime; console.log(`✅ 页面加载成功 (${loadTime}ms)`); // 等待页面完全初始化 await page.waitForTimeout(3000); // 检查Canvas元素 const canvasCount = await page.locator('canvas').count(); console.log(`🎨 Canvas数量: ${canvasCount}`); let canvasInfo = '未找到'; if (canvasCount > 0) { const canvas = page.locator('canvas').first(); const box = await canvas.boundingBox(); if (box) { canvasInfo = `${Math.round(box.width)}x${Math.round(box.height)}`; console.log(`📐 Canvas尺寸: ${canvasInfo}`); } } // 查找开始按钮 const buttonSelectors = [ '#start-btn', '#start-button', 'button:has-text("开始")', 'button:has-text("Start")', 'button:has-text("▶️")', 'button:has-text("Play")', 'button' ]; let startButton = null; let buttonSelector = ''; for (const selector of buttonSelectors) { const count = await page.locator(selector).count(); if (count > 0) { startButton = page.locator(selector).first(); buttonSelector = selector; console.log(`🔘 找到按钮: ${selector} (${count}个)`); break; } } // 点击开始按钮或按空格键 let buttonClicked = false; if (startButton) { try { await startButton.click({ timeout: 3000 }); buttonClicked = true; console.log('✅ 点击开始按钮'); } catch (clickError) { console.log(`⚠️ 按钮点击失败: ${clickError.message}`); } } else { console.log('⚠️ 未找到开始按钮,尝试按空格键'); await page.keyboard.press(' '); } // 等待游戏开始 await page.waitForTimeout(2000); // 再次尝试点击(有些版本需要双击) if (!buttonClicked && startButton) { try { await startButton.click({ timeout: 2000 }); console.log('✅ 第二次点击开始按钮'); buttonClicked = true; } catch (e) { // 忽略第二次点击失败 } } // 等待游戏运行 await page.waitForTimeout(5000); // 等待5秒让游戏运行 // 分析日志 const spawnKeywords = ['生成', 'spawn', 'new piece', '方块', 'piece']; const moveKeywords = ['移动', 'move', '下落', 'drop', '位置', '位置', 'x=', 'y=']; const errorKeywords = ['error', '❌', '失败', '未找到', 'undefined', 'NaN', 'TypeError']; const spawnLogs = logs.filter(log => spawnKeywords.some(keyword => log.text.toLowerCase().includes(keyword.toLowerCase())) ); const moveLogs = logs.filter(log => moveKeywords.some(keyword => log.text.toLowerCase().includes(keyword.toLowerCase())) ); const errorLogs = logs.filter(log => errorKeywords.some(keyword => log.text.toLowerCase().includes(keyword.toLowerCase())) ); console.log(`📊 日志统计:`); console.log(` 总日志: ${logs.length}条`); console.log(` 生成日志: ${spawnLogs.length}条`); console.log(` 移动日志: ${moveLogs.length}条`); console.log(` 错误日志: ${errorLogs.length}条`); // 显示关键日志 if (spawnLogs.length > 0) { const lastSpawn = spawnLogs[spawnLogs.length - 1]; console.log(`📝 最后生成: ${lastSpawn.text.substring(0, 100)}`); } if (moveLogs.length > 0) { const recentMoves = moveLogs.slice(-3); console.log(`📝 最近移动:`); recentMoves.forEach((log, i) => { console.log(` ${i+1}. ${log.text.substring(0, 80)}`); }); } if (errorLogs.length > 0) { console.log(`⚠️ 关键错误:`); errorLogs.slice(0, 3).forEach((log, i) => { console.log(` ${i+1}. ${log.text.substring(0, 100)}`); }); } // 截图 const screenshotName = `${version.type}_${version.name.replace(/\s+/g, '_')}.png`; const screenshotPath = path.join(OUTPUT_DIR, screenshotName); try { await page.screenshot({ path: screenshotPath, fullPage: true, timeout: 5000 }); console.log(`📸 截图保存: ${screenshotPath}`); } catch (screenshotError) { console.log(`⚠️ 截图失败: ${screenshotError.message}`); } // 判断测试结果 let status = 'unknown'; let score = 0; let details = []; // 评分系统 if (canvasCount > 0) score += 20; if (buttonClicked) score += 20; if (spawnLogs.length > 0) score += 30; if (moveLogs.length > 0) score += 30; if (errorLogs.length === 0) score += 10; // 状态判断 if (score >= 90) { status = 'excellent'; details.push('🎉 完美工作'); } else if (score >= 70) { status = 'good'; details.push('✅ 工作良好'); } else if (score >= 50) { status = 'partial'; details.push('⚠️ 部分工作'); } else if (score >= 30) { status = 'poor'; details.push('❌ 工作不佳'); } else { status = 'failed'; details.push('💥 完全失败'); } if (spawnLogs.length === 0) details.push('方块未生成'); if (moveLogs.length === 0) details.push('方块未移动'); if (errorLogs.length > 0) details.push(`${errorLogs.length}个错误`); console.log(`📈 测试评分: ${score}/100 (${status})`); console.log(`📋 详情: ${details.join(', ')}`); // 收集测试结果 return { ...version, status, score, details: details.join('; '), canvasCount, canvasSize: canvasInfo, buttonClicked, spawnLogs: spawnLogs.length, moveLogs: moveLogs.length, errorLogs: errorLogs.length, totalLogs: logs.length, loadTime, screenshot: screenshotPath, url, logs: logs.slice(-20).map(log => `${log.type}: ${log.text}`), // 最后20条日志 errors: errors.slice(0, 5) // 前5个错误 }; } catch (error) { console.log(`💥 测试异常: ${error.message}`); return { ...version, status: 'error', score: 0, details: `测试异常: ${error.message}`, canvasCount: 0, canvasSize: '未知', buttonClicked: false, spawnLogs: 0, moveLogs: 0, errorLogs: 1, totalLogs: 0, loadTime: 0, screenshot: '', url: BASE_URL + version.file, logs: [], errors: [error.message] }; } finally { await page.close(); } } async function main() { console.log('🚀 开始全面自动化测试俄罗斯方块游戏'); console.log(`测试时间: ${new Date().toLocaleString()}`); console.log(`测试版本数: ${ALL_VERSIONS.length}`); console.log(`输出目录: ${OUTPUT_DIR}`); console.log('='.repeat(80)); const browser = await chromium.launch({ headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--disable-gpu' ] }); const results = []; const startTime = Date.now(); try { // 按优先级排序测试 const sortedVersions = [...ALL_VERSIONS].sort((a, b) => a.priority - b.priority); for (let i = 0; i < sortedVersions.length; i++) { const version = sortedVersions[i]; const result = await testVersion(browser, version, i + 1, sortedVersions.length); results.push(result); // 版本间间隔 await new Promise(resolve => setTimeout(resolve, 1000)); } const totalTime = Date.now() - startTime; console.log(`\n✅ 所有测试完成! 总耗时: ${(totalTime / 1000).toFixed(1)}秒`); // 生成详细报告 generateReport(results, totalTime); } catch (error) { console.error(`💥 主测试过程异常: ${error.message}`); } finally { await browser.close(); } } function generateReport(results, totalTime) { console.log('\n' + '='.repeat(80)); console.log('📋 俄罗斯方块游戏自动化测试报告'); console.log('='.repeat(80)); // 按类型和状态分组 const byType = { canvas2d: results.filter(r => r.type === 'canvas2d'), playcanvas: results.filter(r => r.type === 'playcanvas'), mixed: results.filter(r => r.type === 'mixed'), default: results.filter(r => r.type === 'default') }; const byStatus = { excellent: results.filter(r => r.status === 'excellent'), good: results.filter(r => r.status === 'good'), partial: results.filter(r => r.status === 'partial'), poor: results.filter(r => r.status === 'poor'), failed: results.filter(r => r.status === 'failed'), error: results.filter(r => r.status === 'error') }; // 统计信息 console.log(`\n📊 总体统计:`); console.log(` 测试版本: ${results.length}个`); console.log(` 测试耗时: ${(totalTime / 1000).toFixed(1)}秒`); console.log(` 成功版本: ${byStatus.excellent.length + byStatus.good.length}`); console.log(` 部分成功: ${byStatus.partial.length}`); console.log(` 失败版本: ${byStatus.poor.length + byStatus.failed.length + byStatus.error.length}`); console.log(`\n📊 按类型统计:`); Object.entries(byType).forEach(([type, versions]) => { if (versions.length > 0) { const excellent = versions.filter(v => v.status === 'excellent').length; const good = versions.filter(v => v.status === 'good').length; console.log(` ${type}: ${versions.length}个 (优秀:${excellent}, 良好:${good})`); } }); // 最佳版本推荐 const excellentVersions = [...byStatus.excellent].sort((a, b) => b.score - a.score); const goodVersions = [...byStatus.good].sort((a, b) => b.score - a.score); console.log('\n' + '='.repeat(80)); console.log('🏆 最佳版本推荐'); console.log('='.repeat(80)); if (excellentVersions.length > 0) { console.log('\n🎉 优秀版本 (评分≥90):'); excellentVersions.slice(0, 3).forEach((version, i) => { console.log(` ${i+1}. ${version.name}`); console.log(` 文件: ${version.file}`); console.log(` 评分: ${version.score}/100`); console.log(` Canvas: ${version.canvasSize}`); console.log(` 生成: ${version.spawnLogs}次, 移动: ${version.moveLogs}次`); console.log(` 错误: ${version.errorLogs}个`); console.log(` 访问: ${version.url}`); console.log(''); }); } if (goodVersions.length > 0 && excellentVersions.length < 3) { console.log('\n✅ 良好版本 (评分≥70):'); goodVersions.slice(0, 3).forEach((version, i) => { console.log(` ${i+1}. ${version.name}`); console.log(` 评分: ${version.score}/100`); console.log(` 详情: ${version.details}`); console.log(` 访问: ${version.url}`); console.log(''); }); } // 问题版本 const problemVersions = results.filter(r => r.status === 'poor' || r.status === 'failed' || r.status === 'error' ); if (problemVersions.length > 0) { console.log('\n' + '='.repeat(80)); console.log('⚠️ 问题版本需要修复'); console.log('='.repeat(80)); problemVersions.slice(0, 5).forEach((version, i) => { console.log(` ${i+1}. ${version.name} (${version.file})`); console.log(` 状态: ${version.status}, 评分: ${version.score}/100`); console.log(` 问题: ${version.details}`); if (version.errors && version.errors.length > 0) { console.log(` 错误: ${version.errors[0]}`); } console.log(''); }); } // 生成CSV报告 generateCsvReport(results); // 生成HTML报告 generateHtmlReport(results, excellentVersions); console.log(`\n📁 所有截图保存在: ${OUTPUT_DIR}`); console.log(`📄 CSV报告: ${path.join(OUTPUT_DIR, 'tetris_test_results.csv')}`); console.log(`📄 HTML报告: ${path.join(OUTPUT_DIR, 'tetris_test_report.html')}`); } function generateCsvReport(results) { const csvPath = path.join(OUTPUT_DIR, 'tetris_test_results.csv'); const headers = [ '序号', '名称', '文件', '类型', '状态', '评分', 'Canvas数量', 'Canvas尺寸', '按钮点击', '生成次数', '移动次数', '错误数', '总日志数', '加载时间(ms)', '详情', 'URL' ]; const csvRows = [ headers.join(','), ...results.map((result, index) => [ index + 1, `"${result.name}"`, `"${result.file}"`, `"${result.type}"`, `"${result.status}"`, result.score, result.canvasCount, `"${result.canvasSize}"`, result.buttonClicked ? '是' : '否', result.spawnLogs, result.moveLogs, result.errorLogs, result.totalLogs, result.loadTime, `"${result.details}"`, `"${result.url}"` ].join(',')) ]; fs.writeFileSync(csvPath, csvRows.join('\n')); console.log(`✅ CSV报告已生成: ${csvPath}`); } function generateHtmlReport(results, bestVersions) { const htmlPath = path.join(OUTPUT_DIR, 'tetris_test_report.html'); const statusColors = { excellent: '#4CAF50', good: '#8BC34A', partial: '#FFC107', poor: '#FF9800', failed: '#F44336', error: '#9C27B0' }; const statusTexts = { excellent: '优秀', good: '良好', partial: '部分工作', poor: '工作不佳', failed: '失败', error: '异常' }; const html = `
评分: ${version.score}/100
Canvas尺寸: ${version.canvasSize}
游戏表现: 方块生成${version.spawnLogs}次,移动${version.moveLogs}次
错误数量: ${version.errorLogs}个
访问地址: ${version.url}
${version.screenshot ? `测试截图:
| # | 版本名称 | 文件 | 类型 | 状态 | 评分 | Canvas | 生成/移动 | 错误 | 详情 |
|---|---|---|---|---|---|---|---|---|---|
| ${index + 1} | ${result.name} | ${result.file} |
${result.type} | ${statusTexts[result.status] || result.status} | ${result.score}/100 | ${result.canvasSize} | ${result.spawnLogs}/${result.moveLogs} | ${result.errorLogs} | ${result.details} |