384 lines
10 KiB
JavaScript
384 lines
10 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
function loadResults(filename) {
|
|
const filePath = path.join('test-results', filename);
|
|
if (!fs.existsSync(filePath)) {
|
|
return null;
|
|
}
|
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
}
|
|
|
|
function generateHTMLReport() {
|
|
const performance = loadResults('performance-summary.json');
|
|
const seo = loadResults('seo-summary.json');
|
|
const accessibility = loadResults('accessibility-summary.json');
|
|
const forms = loadResults('form-validation-summary.json');
|
|
|
|
const timestamp = new Date().toLocaleString('zh-CN');
|
|
|
|
const html = `<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>开发环境测试报告 - ${timestamp}</title>
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
background: #f5f5f5;
|
|
}
|
|
.header {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
padding: 30px;
|
|
border-radius: 10px;
|
|
margin-bottom: 30px;
|
|
}
|
|
.header h1 {
|
|
margin: 0 0 10px 0;
|
|
}
|
|
.header p {
|
|
margin: 0;
|
|
opacity: 0.9;
|
|
}
|
|
.section {
|
|
background: white;
|
|
padding: 25px;
|
|
border-radius: 10px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
}
|
|
.section h2 {
|
|
margin-top: 0;
|
|
color: #333;
|
|
border-bottom: 3px solid #667eea;
|
|
padding-bottom: 10px;
|
|
}
|
|
.metric {
|
|
display: inline-block;
|
|
padding: 10px 20px;
|
|
margin: 5px;
|
|
border-radius: 5px;
|
|
font-weight: bold;
|
|
}
|
|
.metric.success {
|
|
background: #10b981;
|
|
color: white;
|
|
}
|
|
.metric.warning {
|
|
background: #f59e0b;
|
|
color: white;
|
|
}
|
|
.metric.danger {
|
|
background: #ef4444;
|
|
color: white;
|
|
}
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 15px;
|
|
}
|
|
th, td {
|
|
padding: 12px;
|
|
text-align: left;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
}
|
|
th {
|
|
background: #f9fafb;
|
|
font-weight: bold;
|
|
}
|
|
.summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 20px;
|
|
margin-top: 20px;
|
|
}
|
|
.summary-card {
|
|
background: #f9fafb;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
border-left: 4px solid #667eea;
|
|
}
|
|
.summary-card h3 {
|
|
margin-top: 0;
|
|
color: #667eea;
|
|
}
|
|
.summary-card .value {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
ul {
|
|
line-height: 1.8;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>🚀 开发环境测试报告</h1>
|
|
<p>生成时间: ${timestamp}</p>
|
|
</div>
|
|
|
|
${performance ? generatePerformanceSection(performance) : ''}
|
|
${seo ? generateSEOSection(seo) : ''}
|
|
${accessibility ? generateAccessibilitySection(accessibility) : ''}
|
|
${forms ? generateFormSection(forms) : ''}
|
|
|
|
<div class="section">
|
|
<h2>📊 总体评估</h2>
|
|
<div class="summary">
|
|
${generateOverallSummary(performance, seo, accessibility, forms)}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>📝 建议和后续步骤</h2>
|
|
${generateRecommendations(performance, seo, accessibility, forms)}
|
|
</div>
|
|
</body>
|
|
</html>`;
|
|
|
|
const outputPath = 'test-results/test-report.html';
|
|
fs.writeFileSync(outputPath, html);
|
|
return outputPath;
|
|
}
|
|
|
|
function generatePerformanceSection(data) {
|
|
const avgScore = data.reduce((sum, page) =>
|
|
sum + (page.metrics?.performance || 0), 0) / data.length;
|
|
|
|
return `
|
|
<div class="section">
|
|
<h2>⚡ 性能测试结果</h2>
|
|
<div class="summary">
|
|
<div class="summary-card">
|
|
<h3>平均分数</h3>
|
|
<div class="value">${avgScore.toFixed(1)}</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<h3>测试页面数</h3>
|
|
<div class="value">${data.length}</div>
|
|
</div>
|
|
</div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>页面</th>
|
|
<th>性能</th>
|
|
<th>可访问性</th>
|
|
<th>最佳实践</th>
|
|
<th>SEO</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${data.map(page => `
|
|
<tr>
|
|
<td>${page.page}</td>
|
|
<td><span class="metric ${page.metrics?.performance >= 80 ? 'success' : 'warning'}">${page.metrics?.performance?.toFixed(0) || 'N/A'}</span></td>
|
|
<td><span class="metric ${page.metrics?.accessibility >= 80 ? 'success' : 'warning'}">${page.metrics?.accessibility?.toFixed(0) || 'N/A'}</span></td>
|
|
<td><span class="metric ${page.metrics?.bestPractices >= 80 ? 'success' : 'warning'}">${page.metrics?.bestPractices?.toFixed(0) || 'N/A'}</span></td>
|
|
<td><span class="metric ${page.metrics?.seo >= 80 ? 'success' : 'warning'}">${page.metrics?.seo?.toFixed(0) || 'N/A'}</span></td>
|
|
</tr>
|
|
`).join('')}
|
|
</tbody>
|
|
</table>
|
|
</div>`;
|
|
}
|
|
|
|
function generateSEOSection(data) {
|
|
const passRate = ((data.totalPages - data.pagesWithIssues) / data.totalPages * 100);
|
|
|
|
return `
|
|
<div class="section">
|
|
<h2>🔍 SEO检查结果</h2>
|
|
<div class="summary">
|
|
<div class="summary-card">
|
|
<h3>通过率</h3>
|
|
<div class="value">${passRate.toFixed(1)}%</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<h3>总问题数</h3>
|
|
<div class="value">${data.totalIssues}</div>
|
|
</div>
|
|
</div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>严重程度</th>
|
|
<th>数量</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>🔴 高</td>
|
|
<td>${data.severity.high}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>🟡 中</td>
|
|
<td>${data.severity.medium}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>🟢 低</td>
|
|
<td>${data.severity.low}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>`;
|
|
}
|
|
|
|
function generateAccessibilitySection(data) {
|
|
const avgScore = data.summary.averageScore;
|
|
const passRate = ((data.totalPages - data.pagesWithIssues) / data.totalPages * 100);
|
|
|
|
return `
|
|
<div class="section">
|
|
<h2>♿ 可访问性测试结果</h2>
|
|
<div class="summary">
|
|
<div class="summary-card">
|
|
<h3>平均分数</h3>
|
|
<div class="value">${avgScore}</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<h3>达标率</h3>
|
|
<div class="value">${passRate.toFixed(1)}%</div>
|
|
</div>
|
|
</div>
|
|
<p>标准: WCAG 2.1 AA</p>
|
|
<p>总违规数: ${data.summary.totalViolations}</p>
|
|
</div>`;
|
|
}
|
|
|
|
function generateFormSection(data) {
|
|
return `
|
|
<div class="section">
|
|
<h2>📝 表单验证结果</h2>
|
|
<div class="summary">
|
|
<div class="summary-card">
|
|
<h3>通过率</h3>
|
|
<div class="value">${data.summary.passRate}%</div>
|
|
</div>
|
|
<div class="summary-card">
|
|
<h3>总测试数</h3>
|
|
<div class="value">${data.summary.totalTests}</div>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
function generateOverallSummary(performance, seo, accessibility, forms) {
|
|
const sections = [];
|
|
|
|
if (performance) {
|
|
const avgScore = performance.reduce((sum, page) =>
|
|
sum + (page.metrics?.performance || 0), 0) / performance.length;
|
|
sections.push(`
|
|
<div class="summary-card">
|
|
<h3>性能</h3>
|
|
<div class="value">${avgScore.toFixed(1)}</div>
|
|
</div>`);
|
|
}
|
|
|
|
if (seo) {
|
|
const passRate = ((seo.totalPages - seo.pagesWithIssues) / seo.totalPages * 100);
|
|
sections.push(`
|
|
<div class="summary-card">
|
|
<h3>SEO</h3>
|
|
<div class="value">${passRate.toFixed(1)}%</div>
|
|
</div>`);
|
|
}
|
|
|
|
if (accessibility) {
|
|
sections.push(`
|
|
<div class="summary-card">
|
|
<h3>可访问性</h3>
|
|
<div class="value">${accessibility.summary.averageScore}</div>
|
|
</div>`);
|
|
}
|
|
|
|
if (forms) {
|
|
sections.push(`
|
|
<div class="summary-card">
|
|
<h3>表单</h3>
|
|
<div class="value">${forms.summary.passRate}%</div>
|
|
</div>`);
|
|
}
|
|
|
|
return sections.join('');
|
|
}
|
|
|
|
function generateRecommendations(performance, seo, accessibility, forms) {
|
|
const recommendations = [];
|
|
|
|
if (performance) {
|
|
const lowPerfPages = performance.filter(p => p.metrics?.performance < 80);
|
|
if (lowPerfPages.length > 0) {
|
|
recommendations.push(`
|
|
<h3>⚡ 性能优化</h3>
|
|
<ul>
|
|
<li>优化以下页面的加载速度: ${lowPerfPages.map(p => p.page).join(', ')}</li>
|
|
<li>实施图片懒加载和压缩</li>
|
|
<li>优化CSS和JavaScript资源</li>
|
|
<li>配置CDN加速</li>
|
|
</ul>`);
|
|
}
|
|
}
|
|
|
|
if (seo) {
|
|
if (seo.totalIssues > 0) {
|
|
recommendations.push(`
|
|
<h3>🔍 SEO改进</h3>
|
|
<ul>
|
|
<li>修复 ${seo.severity.high} 个高优先级SEO问题</li>
|
|
<li>完善所有页面的Meta标签</li>
|
|
<li>添加Open Graph标签</li>
|
|
<li>生成并提交sitemap.xml</li>
|
|
<li>配置robots.txt</li>
|
|
</ul>`);
|
|
}
|
|
}
|
|
|
|
if (accessibility) {
|
|
const lowAccessPages = accessibility.pages.filter(p => parseFloat(p.score) < 80);
|
|
if (lowAccessPages.length > 0) {
|
|
recommendations.push(`
|
|
<h3>♿ 可访问性改进</h3>
|
|
<ul>
|
|
<li>修复以下页面的可访问性问题: ${lowAccessPages.map(p => p.page).join(', ')}</li>
|
|
<li>添加ARIA标签</li>
|
|
<li>改进键盘导航</li>
|
|
<li>优化颜色对比度</li>
|
|
<li>确保所有交互元素可访问</li>
|
|
</ul>`);
|
|
}
|
|
}
|
|
|
|
if (forms) {
|
|
if (parseFloat(forms.summary.passRate) < 100) {
|
|
recommendations.push(`
|
|
<h3>📝 表单改进</h3>
|
|
<ul>
|
|
<li>修复表单验证逻辑</li>
|
|
<li>改进错误提示信息</li>
|
|
<li>添加提交成功反馈</li>
|
|
<li>确保表单在移动端正常工作</li>
|
|
</ul>`);
|
|
}
|
|
}
|
|
|
|
return recommendations.join('') || '<p>✅ 所有测试都通过了!网站已准备好上线。</p>';
|
|
}
|
|
|
|
async function main() {
|
|
console.log('📊 生成综合测试报告...\n');
|
|
|
|
const reportPath = generateHTMLReport();
|
|
|
|
console.log(`✅ 报告已生成: ${reportPath}`);
|
|
console.log(`💡 在浏览器中打开查看详细报告`);
|
|
}
|
|
|
|
main().catch(console.error); |