Files
ccdi/doc/test-data/员工导入状态持久化功能测试用例.js

489 lines
14 KiB
JavaScript
Raw Normal View History

feat: 完成员工导入结果跨页面持久化功能 功能概述: - 使用localStorage存储最近一次导入任务信息 - 支持切换菜单后查看上一次的导入失败记录 - 自动过期处理(7天) - 完整的错误处理和用户友好的提示信息 - 新增清除历史记录功能 核心实现: - saveImportTaskToStorage: 保存导入状态到localStorage - getImportTaskFromStorage: 读取并验证导入状态 - clearImportTaskFromStorage: 清除localStorage数据 - restoreImportState: 页面加载时恢复导入状态 - getLastImportTooltip: 获取导入时间提示 - clearImportHistory: 用户手动清除历史记录 导入流程增强: - handleFileSuccess: 保存初始状态,清除旧数据 - handleImportComplete: 保存完整状态,更新UI - startImportStatusPolling: 添加5分钟超时机制 错误处理增强: - getFailureList: 分类处理404/500/网络错误 - 404错误时自动清除localStorage并隐藏按钮 - 友好的用户提示信息 UI优化: - lastImportInfo计算属性显示导入统计 - 失败记录按钮tooltip显示导入时间 - 失败记录对话框显示完整统计信息 - 对话框添加清除历史记录按钮 测试场景: - 导入成功无失败后刷新页面 - 导入有失败后刷新页面 - 导入有失败后切换菜单 - 新导入覆盖旧记录 - 手动清除历史记录 - localStorage过期处理 相关提交: - b932a7d docs: 添加员工导入结果跨页面持久化设计文档 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 13:40:39 +08:00
/**
* 员工导入状态持久化功能测试用例
*
* 测试目标验证导入状态跨页面持久化功能
*
* 测试场景
* 1. 导入成功场景全部成功
* 2. 导入部分失败场景
* 3. 刷新页面后状态恢复
* 4. localStorage过期处理
* 5. 清除导入历史功能
*/
const BASE_URL = 'http://localhost:8080';
// 测试账号
const TEST_CREDENTIALS = {
username: 'admin',
password: 'admin123'
};
let authToken = '';
/**
* 登录获取token
*/
async function login() {
console.log('\n=== 步骤1: 登录系统 ===');
const response = await fetch(`${BASE_URL}/login/test`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(TEST_CREDENTIALS)
});
const result = await response.json();
if (result.code === 200) {
authToken = result.token;
console.log('✅ 登录成功获取到token');
return true;
} else {
console.error('❌ 登录失败:', result.msg);
return false;
}
}
/**
* 模拟导入场景不实际上传文件直接构造数据
*/
function simulateImportSuccess() {
console.log('\n=== 步骤2: 模拟导入成功场景 ===');
// 模拟后端返回的状态数据
const mockSuccessResult = {
taskId: 'task_' + Date.now(),
status: 'SUCCESS',
totalCount: 100,
successCount: 100,
failureCount: 0,
progress: 100,
message: '导入完成'
};
console.log('模拟数据:', mockSuccessResult);
// 模拟前端保存到localStorage
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));
console.log('✅ 已保存导入任务到localStorage');
console.log('保存的数据:', JSON.stringify(taskData, null, 2));
return mockSuccessResult;
}
/**
* 模拟导入部分失败场景
*/
function simulateImportWithFailures() {
console.log('\n=== 步骤3: 模拟导入部分失败场景 ===');
// 模拟后端返回的状态数据
const mockFailureResult = {
taskId: 'task_' + Date.now(),
status: 'SUCCESS',
totalCount: 100,
successCount: 95,
failureCount: 5,
progress: 100,
message: '导入完成'
};
console.log('模拟数据:', mockFailureResult);
// 模拟前端保存到localStorage
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));
console.log('✅ 已保存导入任务到localStorage包含失败记录');
console.log('保存的数据:', JSON.stringify(taskData, null, 2));
return mockFailureResult;
}
/**
* 验证localStorage中的数据
*/
function verifyStorageData() {
console.log('\n=== 步骤4: 验证localStorage数据 ===');
try {
const data = localStorage.getItem('employee_import_last_task');
if (!data) {
console.log('❌ localStorage中没有找到导入任务数据');
return null;
}
const task = JSON.parse(data);
console.log('✅ 成功读取localStorage中的数据');
console.log('读取的数据:', JSON.stringify(task, null, 2));
// 验证必要字段
const requiredFields = ['taskId', 'status', 'hasFailures', 'totalCount', 'successCount', 'failureCount', 'saveTime'];
const missingFields = requiredFields.filter(field => !(field in task));
if (missingFields.length > 0) {
console.error('❌ 缺少必要字段:', missingFields);
return null;
}
console.log('✅ 所有必要字段都存在');
// 验证字段类型
if (typeof task.taskId !== 'string') {
console.error('❌ taskId字段类型错误期望string实际:', typeof task.taskId);
return null;
}
if (typeof task.status !== 'string') {
console.error('❌ status字段类型错误期望string实际:', typeof task.status);
return null;
}
if (typeof task.hasFailures !== 'boolean') {
console.error('❌ hasFailures字段类型错误期望boolean实际:', typeof task.hasFailures);
return null;
}
if (typeof task.saveTime !== 'number') {
console.error('❌ saveTime字段类型错误期望number实际:', typeof task.saveTime);
return null;
}
console.log('✅ 所有字段类型正确');
// 验证时间戳合理性
const now = Date.now();
const timeDiff = now - task.saveTime;
if (timeDiff < 0 || timeDiff > 60000) { // 超过1分钟认为不合理
console.warn('⚠️ saveTime时间戳可能异常当前时间:', now, 'saveTime:', task.saveTime);
} else {
console.log('✅ saveTime时间戳正常');
}
return task;
} catch (error) {
console.error('❌ 解析localStorage数据失败:', error);
return null;
}
}
/**
* 测试状态恢复逻辑
*/
function testRestoreState() {
console.log('\n=== 步骤5: 测试状态恢复逻辑 ===');
const task = verifyStorageData();
if (!task) {
console.log('❌ 无法恢复状态localStorage数据无效');
return false;
}
// 模拟restoreImportState()方法的逻辑
const restoredState = {
showFailureButton: false,
currentTaskId: null
};
if (task.hasFailures && task.taskId) {
restoredState.currentTaskId = task.taskId;
restoredState.showFailureButton = true;
console.log('✅ 检测到失败记录,应该显示"查看导入失败记录"按钮');
console.log(' - showFailureButton:', restoredState.showFailureButton);
console.log(' - currentTaskId:', restoredState.currentTaskId);
} else {
console.log('✅ 没有失败记录,不显示按钮');
console.log(' - showFailureButton:', restoredState.showFailureButton);
console.log(' - currentTaskId:', restoredState.currentTaskId);
}
return restoredState;
}
/**
* 测试过期数据处理
*/
function testExpiredData() {
console.log('\n=== 步骤6: 测试过期数据处理 ===');
// 创建一个8天前的过期数据
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));
console.log('已创建过期数据8天前');
// 模拟getImportTaskFromStorage()的过期检查逻辑
const sevenDays = 7 * 24 * 60 * 60 * 1000;
const isExpired = Date.now() - expiredTask.saveTime > sevenDays;
if (isExpired) {
localStorage.removeItem('employee_import_last_task');
console.log('✅ 检测到过期数据,已清除');
return true;
} else {
console.log('❌ 过期检查逻辑异常');
return false;
}
}
/**
* 测试清除导入历史功能
*/
function testClearHistory() {
console.log('\n=== 步骤7: 测试清除导入历史功能 ===');
// 先保存一些测试数据
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));
console.log('已创建测试数据');
// 模拟clearImportHistory()方法
localStorage.removeItem('employee_import_last_task');
console.log('✅ 已清除导入历史');
// 验证是否真的清除了
const data = localStorage.getItem('employee_import_last_task');
if (data === null) {
console.log('✅ 验证成功:导入历史已完全清除');
return true;
} else {
console.error('❌ 清除失败localStorage中仍有数据');
return false;
}
}
/**
* 测试字段名一致性
*/
function testFieldConsistency() {
console.log('\n=== 步骤8: 测试字段名一致性 ===');
// 模拟ImportStatusVO返回的数据后端
const backendData = {
taskId: 'task_test',
status: 'SUCCESS',
totalCount: 100,
successCount: 95,
failureCount: 5,
progress: 100
};
console.log('后端返回的数据:', backendData);
// 模拟saveImportTaskToStorage()调用的数据(前端)
const frontendSaveData = {
taskId: backendData.taskId,
status: backendData.status,
hasFailures: backendData.failureCount > 0,
totalCount: backendData.totalCount,
successCount: backendData.successCount,
failureCount: backendData.failureCount
};
console.log('前端保存的数据:', frontendSaveData);
// 验证字段映射
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) {
console.log(`${mapping.backend}${mapping.frontend}: 值一致 (${backendValue})`);
} else {
console.error(`${mapping.backend}${mapping.frontend}: 值不一致`);
allMatch = false;
}
});
// 验证saveTime字段
if (frontendSaveData.saveTime || typeof frontendSaveData.saveTime === 'number') {
console.log('✅ saveTime字段存在且为number类型');
} else {
console.error('❌ saveTime字段缺失或类型错误');
allMatch = false;
}
return allMatch;
}
/**
* 运行所有测试
*/
async function runAllTests() {
console.log('╔════════════════════════════════════════════════════════════╗');
console.log('║ 员工导入状态持久化功能 - 完整测试套件 ║');
console.log('╚════════════════════════════════════════════════════════════╝');
// 清理环境
localStorage.removeItem('employee_import_last_task');
console.log('✅ 测试环境已清理');
// 登录
const loginSuccess = await login();
if (!loginSuccess) {
console.error('\n❌ 测试终止:登录失败');
return;
}
const results = {
login: true,
importSuccess: false,
importWithFailures: false,
verifyStorage: false,
restoreState: false,
expiredData: false,
clearHistory: false,
fieldConsistency: false
};
// 测试1: 导入成功场景
try {
simulateImportSuccess();
const task = verifyStorageData();
results.importSuccess = (task !== null && !task.hasFailures);
} catch (error) {
console.error('❌ 导入成功场景测试失败:', error);
}
// 测试2: 导入部分失败场景
try {
localStorage.removeItem('employee_import_last_task'); // 清理
simulateImportWithFailures();
const task = verifyStorageData();
results.importWithFailures = (task !== null && task.hasFailures);
} catch (error) {
console.error('❌ 导入部分失败场景测试失败:', error);
}
// 测试3: 状态恢复
try {
const state = testRestoreState();
results.restoreState = (state !== false && state.showFailureButton === true);
} catch (error) {
console.error('❌ 状态恢复测试失败:', error);
}
// 测试4: 过期数据处理
try {
localStorage.removeItem('employee_import_last_task'); // 清理
results.expiredData = testExpiredData();
} catch (error) {
console.error('❌ 过期数据处理测试失败:', error);
}
// 测试5: 清除导入历史
try {
results.clearHistory = testClearHistory();
} catch (error) {
console.error('❌ 清除导入历史测试失败:', error);
}
// 测试6: 字段名一致性
try {
results.fieldConsistency = testFieldConsistency();
} catch (error) {
console.error('❌ 字段名一致性测试失败:', error);
}
// 输出测试报告
console.log('\n╔════════════════════════════════════════════════════════════╗');
console.log('║ 测试结果汇总 ║');
console.log('╚════════════════════════════════════════════════════════════╝\n');
const testNames = {
login: '用户登录',
importSuccess: '导入成功场景',
importWithFailures: '导入部分失败场景',
restoreState: '状态恢复逻辑',
expiredData: '过期数据处理',
clearHistory: '清除导入历史',
fieldConsistency: '字段名一致性'
};
let passCount = 0;
let failCount = 0;
Object.keys(results).forEach(key => {
const status = results[key] ? '✅ PASS' : '❌ FAIL';
const testName = testNames[key] || key;
console.log(`${status} - ${testName}`);
if (results[key]) {
passCount++;
} else {
failCount++;
}
});
console.log('\n--------------------------------------------------------');
console.log(`总计: ${passCount + failCount} 个测试`);
console.log(`通过: ${passCount}`);
console.log(`失败: ${failCount}`);
console.log('--------------------------------------------------------\n');
if (failCount === 0) {
console.log('🎉 所有测试通过!导入状态持久化功能正常工作。');
} else {
console.log('⚠️ 部分测试失败,请检查相关功能。');
}
// 清理测试数据
localStorage.removeItem('employee_import_last_task');
console.log('✅ 测试数据已清理\n');
}
// 运行测试
runAllTests().catch(error => {
console.error('❌ 测试执行异常:', error);
process.exit(1);
});