paper-burner/tests/performance/phase1-test.html

605 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Phase 1 性能优化测试工具</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
padding: 20px;
background: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 10px;
}
.subtitle {
color: #666;
margin-bottom: 30px;
}
.test-section {
margin-bottom: 40px;
padding: 20px;
background: #f9f9f9;
border-radius: 6px;
border-left: 4px solid #4CAF50;
}
.test-section h2 {
color: #333;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 10px;
}
.test-section h2 .icon {
font-size: 24px;
}
.test-description {
color: #666;
margin-bottom: 20px;
line-height: 1.6;
}
.test-controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
button {
padding: 10px 20px;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}
button:hover {
background: #45a049;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
button.secondary {
background: #2196F3;
}
button.secondary:hover {
background: #0b7dda;
}
button.danger {
background: #f44336;
}
button.danger:hover {
background: #da190b;
}
.results {
background: white;
padding: 15px;
border-radius: 4px;
border: 1px solid #ddd;
}
.result-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #eee;
}
.result-item:last-child {
border-bottom: none;
}
.result-label {
font-weight: 500;
color: #666;
}
.result-value {
font-weight: bold;
color: #333;
}
.result-value.good {
color: #4CAF50;
}
.result-value.bad {
color: #f44336;
}
.log-area {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 12px;
max-height: 300px;
overflow-y: auto;
margin-top: 15px;
}
.log-entry {
margin-bottom: 5px;
}
.log-entry.info {
color: #4FC3F7;
}
.log-entry.success {
color: #4CAF50;
}
.log-entry.warning {
color: #FFA726;
}
.log-entry.error {
color: #EF5350;
}
input[type="text"] {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
width: 300px;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
margin-left: 10px;
}
.status-badge.pending {
background: #FFF9C4;
color: #F57F17;
}
.status-badge.running {
background: #BBDEFB;
color: #1565C0;
}
.status-badge.passed {
background: #C8E6C9;
color: #2E7D32;
}
.status-badge.failed {
background: #FFCDD2;
color: #C62828;
}
.progress-bar {
width: 100%;
height: 4px;
background: #e0e0e0;
border-radius: 2px;
overflow: hidden;
margin: 10px 0;
}
.progress-fill {
height: 100%;
background: #4CAF50;
transition: width 0.3s;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Phase 1 性能优化测试工具</h1>
<p class="subtitle">自动化测试防抖和定时器优化效果</p>
<!-- 测试 1: 防抖功能 -->
<div class="test-section">
<h2>
<span class="icon">🔍</span>
测试 1: 搜索防抖优化
<span class="status-badge pending" id="test1-status">待测试</span>
</h2>
<p class="test-description">
测试历史记录搜索输入的防抖功能。模拟快速输入多个字符,验证是否只触发一次渲染。
<br><strong>预期结果</strong>:快速输入 10 次,只触发 1 次函数调用(减少 90%
</p>
<div class="test-controls">
<button onclick="runDebounceTest()">▶️ 运行测试</button>
<button class="secondary" onclick="clearDebounceLog()">🗑️ 清空日志</button>
</div>
<div class="results" id="debounce-results" style="display: none;">
<div class="result-item">
<span class="result-label">触发次数:</span>
<span class="result-value" id="debounce-triggers">-</span>
</div>
<div class="result-item">
<span class="result-label">实际执行次数:</span>
<span class="result-value good" id="debounce-calls">-</span>
</div>
<div class="result-item">
<span class="result-label">减少比例:</span>
<span class="result-value" id="debounce-reduction">-</span>
</div>
<div class="result-item">
<span class="result-label">总耗时:</span>
<span class="result-value" id="debounce-time">-</span>
</div>
</div>
<div class="log-area" id="debounce-log"></div>
</div>
<!-- 测试 2: 定时器优化 -->
<div class="test-section">
<h2>
<span class="icon">⏱️</span>
测试 2: 轮询定时器优化
<span class="status-badge pending" id="test2-status">待测试</span>
</h2>
<p class="test-description">
测试页面可见性检测功能。验证页面隐藏时是否跳过轮询执行。
<br><strong>预期结果</strong>页面隐藏时轮询函数内部逻辑不执行0 次)
</p>
<div class="test-controls">
<button onclick="startPollingTest()">▶️ 开始监控</button>
<button class="danger" onclick="stopPollingTest()">⏹️ 停止监控</button>
<button class="secondary" onclick="clearPollingLog()">🗑️ 清空日志</button>
</div>
<div class="results" id="polling-results" style="display: none;">
<div class="result-item">
<span class="result-label">页面状态:</span>
<span class="result-value" id="page-visibility">-</span>
</div>
<div class="result-item">
<span class="result-label">定时器运行次数:</span>
<span class="result-value" id="polling-ticks">-</span>
</div>
<div class="result-item">
<span class="result-label">函数执行次数:</span>
<span class="result-value good" id="polling-executions">-</span>
</div>
<div class="result-item">
<span class="result-label">跳过次数(页面隐藏):</span>
<span class="result-value" id="polling-skipped">-</span>
</div>
</div>
<div class="log-area" id="polling-log"></div>
</div>
<!-- 综合报告 -->
<div class="test-section" style="border-left-color: #2196F3;">
<h2>
<span class="icon">📊</span>
综合测试报告
</h2>
<div class="results">
<div class="result-item">
<span class="result-label">防抖测试:</span>
<span class="result-value" id="summary-debounce">未运行</span>
</div>
<div class="result-item">
<span class="result-label">定时器测试:</span>
<span class="result-value" id="summary-polling">未运行</span>
</div>
<div class="result-item">
<span class="result-label">总体评价:</span>
<span class="result-value" id="summary-overall">等待测试...</span>
</div>
</div>
</div>
</div>
<script>
// ============================================
// 测试 1: 防抖功能测试
// ============================================
// 防抖函数实现(从 history.js 复制)
function debounce(fn, delay) {
let timer = null;
return function debounced(...args) {
const context = this;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
fn.apply(context, args);
}, delay);
};
}
let debounceTestData = {
triggers: 0,
calls: 0,
startTime: 0
};
function logDebounce(message, type = 'info') {
const log = document.getElementById('debounce-log');
const entry = document.createElement('div');
entry.className = `log-entry ${type}`;
const timestamp = new Date().toLocaleTimeString();
entry.textContent = `[${timestamp}] ${message}`;
log.appendChild(entry);
log.scrollTop = log.scrollHeight;
}
function clearDebounceLog() {
document.getElementById('debounce-log').innerHTML = '';
}
async function runDebounceTest() {
const status = document.getElementById('test1-status');
status.textContent = '运行中...';
status.className = 'status-badge running';
// 重置数据
debounceTestData = { triggers: 0, calls: 0, startTime: performance.now() };
clearDebounceLog();
logDebounce('=== 开始防抖测试 ===', 'info');
logDebounce('配置: 延迟 300ms, 快速触发 10 次', 'info');
// 创建测试函数
let callCount = 0;
const testFn = debounce(() => {
callCount++;
debounceTestData.calls = callCount;
logDebounce(`✓ 函数执行 (第 ${callCount} 次)`, 'success');
updateDebounceResults();
}, 300);
// 模拟快速输入 10 次
logDebounce('开始模拟快速输入...', 'info');
for (let i = 1; i <= 10; i++) {
debounceTestData.triggers++;
logDebounce(`触发 #${i}`, 'info');
testFn();
await new Promise(resolve => setTimeout(resolve, 50)); // 50ms 间隔
}
logDebounce('等待防抖完成...', 'warning');
// 等待防抖完成
await new Promise(resolve => setTimeout(resolve, 400));
const totalTime = performance.now() - debounceTestData.startTime;
logDebounce(`=== 测试完成 (耗时 ${totalTime.toFixed(0)}ms) ===`, 'success');
// 显示结果
document.getElementById('debounce-results').style.display = 'block';
updateDebounceResults();
// 评估结果
if (debounceTestData.calls === 1) {
status.textContent = '✅ 通过';
status.className = 'status-badge passed';
logDebounce('✓ 测试通过!防抖功能正常工作', 'success');
document.getElementById('summary-debounce').textContent = '✅ 通过';
document.getElementById('summary-debounce').className = 'result-value good';
} else {
status.textContent = '❌ 失败';
status.className = 'status-badge failed';
logDebounce(`✗ 测试失败!预期 1 次执行,实际 ${debounceTestData.calls}`, 'error');
document.getElementById('summary-debounce').textContent = '❌ 失败';
document.getElementById('summary-debounce').className = 'result-value bad';
}
updateSummary();
}
function updateDebounceResults() {
document.getElementById('debounce-triggers').textContent = debounceTestData.triggers;
document.getElementById('debounce-calls').textContent = debounceTestData.calls;
const reduction = ((1 - debounceTestData.calls / debounceTestData.triggers) * 100).toFixed(0);
const reductionEl = document.getElementById('debounce-reduction');
reductionEl.textContent = `${reduction}% ↓`;
reductionEl.className = reduction >= 70 ? 'result-value good' : 'result-value bad';
const time = (performance.now() - debounceTestData.startTime).toFixed(0);
document.getElementById('debounce-time').textContent = `${time}ms`;
}
// ============================================
// 测试 2: 轮询定时器测试
// ============================================
let pollingTestData = {
ticks: 0,
executions: 0,
skipped: 0,
timerId: null,
isActive: false
};
function logPolling(message, type = 'info') {
const log = document.getElementById('polling-log');
const entry = document.createElement('div');
entry.className = `log-entry ${type}`;
const timestamp = new Date().toLocaleTimeString();
entry.textContent = `[${timestamp}] ${message}`;
log.appendChild(entry);
log.scrollTop = log.scrollHeight;
}
function clearPollingLog() {
document.getElementById('polling-log').innerHTML = '';
}
function mockCheckFunction() {
// 模拟批注颜色检查函数
pollingTestData.executions++;
logPolling(`✓ 执行函数逻辑 (第 ${pollingTestData.executions} 次)`, 'success');
updatePollingResults();
}
function startPollingTest() {
const status = document.getElementById('test2-status');
status.textContent = '运行中...';
status.className = 'status-badge running';
// 重置数据
pollingTestData = { ticks: 0, executions: 0, skipped: 0, timerId: null, isActive: true };
clearPollingLog();
logPolling('=== 开始定时器监控 ===', 'info');
logPolling('说明: 请切换到其他标签页测试页面隐藏检测', 'warning');
document.getElementById('polling-results').style.display = 'block';
// 启动轮询(模拟优化后的逻辑)
function poll() {
if (!pollingTestData.isActive) {
logPolling('轮询已停止', 'info');
return;
}
pollingTestData.ticks++;
if (document.hidden) {
pollingTestData.skipped++;
logPolling(`⏭️ 跳过执行 (页面隐藏, 总跳过: ${pollingTestData.skipped})`, 'warning');
} else {
mockCheckFunction();
}
updatePollingResults();
pollingTestData.timerId = setTimeout(poll, 1000);
}
poll();
// 监听页面可见性变化
document.addEventListener('visibilitychange', handleVisibilityChange);
status.textContent = '监控中...';
document.getElementById('summary-polling').textContent = '🔄 运行中';
}
function stopPollingTest() {
const status = document.getElementById('test2-status');
if (!pollingTestData.isActive) {
logPolling('⚠️ 测试未运行', 'warning');
return;
}
pollingTestData.isActive = false;
if (pollingTestData.timerId) {
clearTimeout(pollingTestData.timerId);
pollingTestData.timerId = null;
}
document.removeEventListener('visibilitychange', handleVisibilityChange);
logPolling('=== 测试停止 ===', 'info');
logPolling(`总计: 轮询 ${pollingTestData.ticks} 次, 执行 ${pollingTestData.executions} 次, 跳过 ${pollingTestData.skipped}`, 'success');
// 评估结果
if (pollingTestData.skipped > 0) {
status.textContent = '✅ 通过';
status.className = 'status-badge passed';
logPolling('✓ 测试通过!页面隐藏时正确跳过执行', 'success');
document.getElementById('summary-polling').textContent = '✅ 通过';
document.getElementById('summary-polling').className = 'result-value good';
} else {
status.textContent = '⚠️ 未验证';
status.className = 'status-badge pending';
logPolling('⚠️ 未检测到页面隐藏,请切换标签页后再测试', 'warning');
document.getElementById('summary-polling').textContent = '⚠️ 未充分测试';
document.getElementById('summary-polling').className = 'result-value';
}
updateSummary();
}
function handleVisibilityChange() {
const isHidden = document.hidden;
logPolling(
isHidden ? '🔒 页面隐藏 - 将跳过执行' : '👁️ 页面可见 - 恢复执行',
isHidden ? 'warning' : 'success'
);
updatePollingResults();
}
function updatePollingResults() {
document.getElementById('page-visibility').textContent =
document.hidden ? '🔒 隐藏' : '👁️ 可见';
document.getElementById('polling-ticks').textContent = pollingTestData.ticks;
document.getElementById('polling-executions').textContent = pollingTestData.executions;
document.getElementById('polling-skipped').textContent = pollingTestData.skipped;
}
function updateSummary() {
const debounceResult = document.getElementById('summary-debounce').textContent;
const pollingResult = document.getElementById('summary-polling').textContent;
let overall = '等待测试...';
if (debounceResult.includes('✅') && pollingResult.includes('✅')) {
overall = '🎉 所有测试通过!';
document.getElementById('summary-overall').className = 'result-value good';
} else if (debounceResult.includes('❌') || pollingResult.includes('❌')) {
overall = '❌ 部分测试失败';
document.getElementById('summary-overall').className = 'result-value bad';
} else if (debounceResult.includes('✅') || pollingResult.includes('✅')) {
overall = '⚠️ 部分测试完成';
document.getElementById('summary-overall').className = 'result-value';
}
document.getElementById('summary-overall').textContent = overall;
}
// 初始化
window.addEventListener('load', () => {
console.log('%c🚀 Phase 1 测试工具已加载', 'color: #4CAF50; font-size: 16px; font-weight: bold;');
console.log('%c提示: 在这个页面测试防抖和定时器优化', 'color: #666;');
});
</script>
</body>
</html>