Files
ccdi/doc/test-data/员工导入状态持久化功能测试.html
2026-02-09 14:28:25 +08:00

594 lines
19 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>员工导入状态持久化功能测试</title>
<style>
body {
font-family: 'Courier New', monospace;
max-width: 1200px;
margin: 20px auto;
padding: 20px;
background-color: #f5f5f5;
}
.test-container {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
color: #333;
border-bottom: 3px solid #409eff;
padding-bottom: 10px;
}
h2 {
color: #666;
margin-top: 30px;
}
.test-section {
margin: 20px 0;
padding: 15px;
border-left: 4px solid #409eff;
background: #f9f9f9;
}
.status-pass {
color: #67c23a;
font-weight: bold;
}
.status-fail {
color: #f56c6c;
font-weight: bold;
}
.status-info {
color: #909399;
}
.code {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
font-size: 13px;
line-height: 1.5;
}
.summary {
background: #e6f7ff;
border: 2px solid #1890ff;
border-radius: 8px;
padding: 20px;
margin: 30px 0;
}
.summary h3 {
margin-top: 0;
color: #1890ff;
}
button {
background: #409eff;
color: white;
border: none;
padding: 12px 24px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin: 5px;
}
button:hover {
background: #66b1ff;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
.log {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 4px;
max-height: 400px;
overflow-y: auto;
font-size: 12px;
line-height: 1.4;
}
.log-entry {
margin: 5px 0;
}
.log-success { color: #67c23a; }
.log-error { color: #f56c6c; }
.log-warning { color: #e6a23c; }
.log-info { color: #909399; }
</style>
</head>
<body>
<div class="test-container">
<h1>员工导入状态持久化功能 - 测试套件</h1>
<div style="margin: 20px 0;">
<button id="runAllTests" onclick="runAllTests()">运行所有测试</button>
<button onclick="clearResults()">清除结果</button>
<button onclick="clearLocalStorage()">清除localStorage</button>
</div>
<div id="log" class="log">
<div class="log-entry log-info">点击"运行所有测试"按钮开始测试...</div>
</div>
<div id="results"></div>
</div>
<script>
const BASE_URL = 'http://localhost:8080';
let authToken = '';
function log(message, type = 'info') {
const logDiv = document.getElementById('log');
const entry = document.createElement('div');
entry.className = `log-entry log-${type}`;
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logDiv.appendChild(entry);
logDiv.scrollTop = logDiv.scrollHeight;
}
function clearResults() {
document.getElementById('results').innerHTML = '';
document.getElementById('log').innerHTML = '<div class="log-entry log-info">日志已清除</div>';
}
function clearLocalStorage() {
localStorage.removeItem('employee_import_last_task');
log('localStorage已清除', 'info');
}
function formatJSON(obj) {
return JSON.stringify(obj, null, 2);
}
// 模拟后端ImportStatusVO返回的数据
function simulateImportSuccess() {
log('=== 测试1: 模拟导入成功场景 ===', 'info');
const mockSuccessResult = {
taskId: 'task_' + Date.now(),
status: 'SUCCESS',
totalCount: 100,
successCount: 100,
failureCount: 0,
progress: 100,
message: '导入完成'
};
log('模拟后端返回数据: ' + formatJSON(mockSuccessResult), 'info');
// 模拟前端saveImportTaskToStorage方法
const taskData = {
taskId: mockSuccessResult.taskId,
status: mockSuccessResult.status,
hasFailures: mockSuccessResult.failureCount > 0,
totalCount: mockSuccessResult.totalCount,
successCount: mockSuccessResult.successCount,
failureCount: mockSuccessResult.failureCount,
saveTime: Date.now()
};
localStorage.setItem('employee_import_last_task', JSON.stringify(taskData));
log('✅ 已保存到localStorage', 'success');
log('保存的数据: ' + formatJSON(taskData), 'info');
return mockSuccessResult;
}
function simulateImportWithFailures() {
log('=== 测试2: 模拟导入部分失败场景 ===', 'info');
const mockFailureResult = {
taskId: 'task_' + Date.now(),
status: 'SUCCESS',
totalCount: 100,
successCount: 95,
failureCount: 5,
progress: 100,
message: '导入完成'
};
log('模拟后端返回数据: ' + formatJSON(mockFailureResult), 'info');
const taskData = {
taskId: mockFailureResult.taskId,
status: mockFailureResult.status,
hasFailures: mockFailureResult.failureCount > 0,
totalCount: mockFailureResult.totalCount,
successCount: mockFailureResult.successCount,
failureCount: mockFailureResult.failureCount,
saveTime: Date.now()
};
localStorage.setItem('employee_import_last_task', JSON.stringify(taskData));
log('✅ 已保存到localStorage包含失败记录', 'success');
log('保存的数据: ' + formatJSON(taskData), 'info');
return mockFailureResult;
}
function verifyStorageData() {
log('=== 测试3: 验证localStorage数据 ===', 'info');
try {
const data = localStorage.getItem('employee_import_last_task');
if (!data) {
log('❌ localStorage中没有找到导入任务数据', 'error');
return null;
}
const task = JSON.parse(data);
log('✅ 成功读取localStorage数据', 'success');
log('读取的数据: ' + formatJSON(task), 'info');
// 验证必要字段
const requiredFields = ['taskId', 'status', 'hasFailures', 'totalCount', 'successCount', 'failureCount', 'saveTime'];
const missingFields = requiredFields.filter(field => !(field in task));
if (missingFields.length > 0) {
log('❌ 缺少必要字段: ' + missingFields.join(', '), 'error');
return null;
}
log('✅ 所有必要字段都存在', 'success');
// 验证字段类型
const typeChecks = [
{ field: 'taskId', expected: 'string', actual: typeof task.taskId },
{ field: 'status', expected: 'string', actual: typeof task.status },
{ field: 'hasFailures', expected: 'boolean', actual: typeof task.hasFailures },
{ field: 'saveTime', expected: 'number', actual: typeof task.saveTime }
];
let allTypesCorrect = true;
typeChecks.forEach(check => {
if (check.actual !== check.expected) {
log(`${check.field}字段类型错误,期望${check.expected},实际${check.actual}`, 'error');
allTypesCorrect = false;
}
});
if (allTypesCorrect) {
log('✅ 所有字段类型正确', 'success');
}
// 验证时间戳合理性
const now = Date.now();
const timeDiff = now - task.saveTime;
if (timeDiff < 0 || timeDiff > 60000) {
log('⚠️ saveTime时间戳异常时间差: ' + timeDiff + 'ms', 'warning');
} else {
log('✅ saveTime时间戳正常', 'success');
}
return task;
} catch (error) {
log('❌ 解析localStorage数据失败: ' + error.message, 'error');
return null;
}
}
function testRestoreState() {
log('=== 测试4: 测试状态恢复逻辑 ===', 'info');
const task = verifyStorageData();
if (!task) {
log('❌ 无法恢复状态localStorage数据无效', 'error');
return false;
}
// 模拟restoreImportState()方法的逻辑
const restoredState = {
showFailureButton: false,
currentTaskId: null
};
if (task.hasFailures && task.taskId) {
restoredState.currentTaskId = task.taskId;
restoredState.showFailureButton = true;
log('✅ 检测到失败记录,应该显示"查看导入失败记录"按钮', 'success');
log(' - showFailureButton: ' + restoredState.showFailureButton, 'info');
log(' - currentTaskId: ' + restoredState.currentTaskId, 'info');
} else {
log('✅ 没有失败记录,不显示按钮', 'success');
log(' - showFailureButton: ' + restoredState.showFailureButton, 'info');
log(' - currentTaskId: ' + restoredState.currentTaskId, 'info');
}
return restoredState;
}
function testExpiredData() {
log('=== 测试5: 测试过期数据处理 ===', 'info');
const eightDaysAgo = Date.now() - (8 * 24 * 60 * 60 * 1000);
const expiredTask = {
taskId: 'expired_task',
status: 'SUCCESS',
hasFailures: true,
totalCount: 100,
successCount: 90,
failureCount: 10,
saveTime: eightDaysAgo
};
localStorage.setItem('employee_import_last_task', JSON.stringify(expiredTask));
log('已创建过期数据8天前', 'info');
// 模拟getImportTaskFromStorage()的过期检查逻辑
const sevenDays = 7 * 24 * 60 * 60 * 1000;
const isExpired = Date.now() - expiredTask.saveTime > sevenDays;
if (isExpired) {
localStorage.removeItem('employee_import_last_task');
log('✅ 检测到过期数据,已清除', 'success');
return true;
} else {
log('❌ 过期检查逻辑异常', 'error');
return false;
}
}
function testClearHistory() {
log('=== 测试6: 测试清除导入历史功能 ===', 'info');
const testTask = {
taskId: 'test_clear_task',
status: 'SUCCESS',
hasFailures: true,
totalCount: 50,
successCount: 45,
failureCount: 5,
saveTime: Date.now()
};
localStorage.setItem('employee_import_last_task', JSON.stringify(testTask));
log('已创建测试数据', 'info');
// 模拟clearImportHistory()方法
localStorage.removeItem('employee_import_last_task');
log('✅ 已清除导入历史', 'success');
const data = localStorage.getItem('employee_import_last_task');
if (data === null) {
log('✅ 验证成功:导入历史已完全清除', 'success');
return true;
} else {
log('❌ 清除失败localStorage中仍有数据', 'error');
return false;
}
}
function testFieldConsistency() {
log('=== 测试7: 测试字段名一致性 ===', 'info');
// 模拟后端ImportStatusVO返回的数据
const backendData = {
taskId: 'task_test',
status: 'SUCCESS',
totalCount: 100,
successCount: 95,
failureCount: 5,
progress: 100
};
log('后端ImportStatusVO返回: ' + formatJSON(backendData), 'info');
// 模拟前端saveImportTaskToStorage调用的数据
const frontendSaveData = {
taskId: backendData.taskId,
status: backendData.status,
hasFailures: backendData.failureCount > 0,
totalCount: backendData.totalCount,
successCount: backendData.successCount,
failureCount: backendData.failureCount
};
log('前端保存数据: ' + formatJSON(frontendSaveData), 'info');
// 验证字段映射
const fieldMappings = [
{ backend: 'taskId', frontend: 'taskId' },
{ backend: 'status', frontend: 'status' },
{ backend: 'totalCount', frontend: 'totalCount' },
{ backend: 'successCount', frontend: 'successCount' },
{ backend: 'failureCount', frontend: 'failureCount' }
];
let allMatch = true;
fieldMappings.forEach(mapping => {
const backendValue = backendData[mapping.backend];
const frontendValue = frontendSaveData[mapping.frontend];
if (backendValue === frontendValue) {
log(`${mapping.backend}${mapping.frontend}: 值一致 (${backendValue})`, 'success');
} else {
log(`${mapping.backend}${mapping.frontend}: 值不一致`, 'error');
allMatch = false;
}
});
// 验证saveTime字段会在saveImportTaskToStorage中自动添加
log('✅ saveTime字段在saveImportTaskToStorage方法中自动添加', 'info');
return allMatch;
}
function displayResults(results) {
const resultsDiv = document.getElementById('results');
let html = '<div class="summary">';
html += '<h3>测试结果汇总</h3>';
html += '<table style="width: 100%; border-collapse: collapse;">';
html += '<tr style="border-bottom: 1px solid #ddd;">';
html += '<th style="padding: 10px; text-align: left;">测试项目</th>';
html += '<th style="padding: 10px; text-align: left;">结果</th>';
html += '</tr>';
const testNames = {
importSuccess: '导入成功场景',
importWithFailures: '导入部分失败场景',
restoreState: '状态恢复逻辑',
expiredData: '过期数据处理',
clearHistory: '清除导入历史',
fieldConsistency: '字段名一致性'
};
let passCount = 0;
let failCount = 0;
Object.keys(results).forEach(key => {
const status = results[key] ? '✅ PASS' : '❌ FAIL';
const statusClass = results[key] ? 'status-pass' : 'status-fail';
const testName = testNames[key] || key;
html += '<tr style="border-bottom: 1px solid #eee;">';
html += `<td style="padding: 10px;">${testName}</td>`;
html += `<td style="padding: 10px;" class="${statusClass}">${status}</td>`;
html += '</tr>';
if (results[key]) {
passCount++;
} else {
failCount++;
}
});
html += '</table>';
html += '<p style="margin-top: 20px; font-size: 16px;">';
html += `<strong>总计:</strong> ${passCount + failCount} 个测试 | `;
html += `<span class="status-pass">通过: ${passCount} 个</span> | `;
html += `<span class="status-fail">失败: ${failCount} 个</span>`;
html += '</p>';
if (failCount === 0) {
html += '<p style="margin-top: 15px; font-size: 18px; color: #67c23a;">';
html += '🎉 <strong>所有测试通过!</strong> 导入状态持久化功能正常工作。';
html += '</p>';
} else {
html += '<p style="margin-top: 15px; font-size: 18px; color: #f56c6c;">';
html += '⚠️ <strong>部分测试失败</strong>,请检查相关功能。';
html += '</p>';
}
html += '</div>';
resultsDiv.innerHTML = html;
}
async function runAllTests() {
const btn = document.getElementById('runAllTests');
btn.disabled = true;
btn.textContent = '测试运行中...';
document.getElementById('log').innerHTML = '';
document.getElementById('results').innerHTML = '';
log('╔════════════════════════════════════════════════════════════╗', 'info');
log('║ 员工导入状态持久化功能 - 完整测试套件 ║', 'info');
log('╚════════════════════════════════════════════════════════════╝', 'info');
// 清理环境
localStorage.removeItem('employee_import_last_task');
log('✅ 测试环境已清理', 'success');
const results = {
importSuccess: false,
importWithFailures: false,
restoreState: false,
expiredData: false,
clearHistory: false,
fieldConsistency: false
};
// 测试1: 导入成功场景
try {
localStorage.removeItem('employee_import_last_task');
simulateImportSuccess();
const task = verifyStorageData();
results.importSuccess = (task !== null && !task.hasFailures);
} catch (error) {
log('❌ 导入成功场景测试失败: ' + error.message, 'error');
}
// 测试2: 导入部分失败场景
try {
localStorage.removeItem('employee_import_last_task');
simulateImportWithFailures();
const task = verifyStorageData();
results.importWithFailures = (task !== null && task.hasFailures);
} catch (error) {
log('❌ 导入部分失败场景测试失败: ' + error.message, 'error');
}
// 测试3: 状态恢复
try {
const state = testRestoreState();
results.restoreState = (state !== false && state.showFailureButton === true);
} catch (error) {
log('❌ 状态恢复测试失败: ' + error.message, 'error');
}
// 测试4: 过期数据处理
try {
localStorage.removeItem('employee_import_last_task');
results.expiredData = testExpiredData();
} catch (error) {
log('❌ 过期数据处理测试失败: ' + error.message, 'error');
}
// 测试5: 清除导入历史
try {
results.clearHistory = testClearHistory();
} catch (error) {
log('❌ 清除导入历史测试失败: ' + error.message, 'error');
}
// 测试6: 字段名一致性
try {
localStorage.removeItem('employee_import_last_task');
results.fieldConsistency = testFieldConsistency();
} catch (error) {
log('❌ 字段名一致性测试失败: ' + error.message, 'error');
}
log('╔════════════════════════════════════════════════════════════╗', 'info');
log('║ 测试完成 ║', 'info');
log('╚════════════════════════════════════════════════════════════╝', 'info');
displayResults(results);
// 清理测试数据
localStorage.removeItem('employee_import_last_task');
log('✅ 测试数据已清理', 'success');
btn.disabled = false;
btn.textContent = '运行所有测试';
}
</script>
</body>
</html>