Files
railwaysystem/build_ui/tests/unit/attendanceSigninEntry.spec.mjs
T

191 lines
6.3 KiB
JavaScript

import assert from 'node:assert';
console.log('=== Testing Attendance Sign-in Entry & Roster Semantics ===');
// 1. 测试考勤状态语义映射:未签到 (NO_CHECKIN) 必须严格与缺勤 (0) 区分
function formatAttendanceTag(status) {
if (status === null || status === undefined) {
return { text: '未签到', type: 'info' };
}
switch (status) {
case 0:
return { text: '缺勤', type: 'danger' };
case 1:
return { text: '正常', type: 'success' };
case 2:
return { text: '迟到', type: 'warning' };
case 3:
return { text: '早退', type: 'warning' };
case 4:
return { text: '请假', type: 'primary' };
default:
return { text: '-', type: '' };
}
}
// A. 验证未打卡学员渲染为“未签到”,绝不等于“缺勤”
{
const tagNull = formatAttendanceTag(null);
assert.strictEqual(tagNull.text, '未签到');
assert.strictEqual(tagNull.type, 'info');
const tagUndefined = formatAttendanceTag(undefined);
assert.strictEqual(tagUndefined.text, '未签到');
const tagAbsent = formatAttendanceTag(0);
assert.strictEqual(tagAbsent.text, '缺勤');
assert.strictEqual(tagAbsent.type, 'danger');
// 红线:未签到和缺勤完全不同
assert.notStrictEqual(tagNull.text, tagAbsent.text);
assert.notStrictEqual(tagNull.type, tagAbsent.type);
console.log('✔ PASS: NO_CHECKIN is strictly distinct from ABSENT (0)');
}
// 2. 按钮可见性与防重复规则
function getRowActionButtons(row) {
return {
signIn: !!row.canSignIn,
signOut: !!row.canSignOut,
adjust: !!row.canAdjust
};
}
// A. 无 checkin 记录学员:签到按钮可见,签退不可见
{
const uncheckRow = {
enrollmentId: 101,
checkinId: null,
attendanceStatus: null,
signInTime: null,
signOutTime: null,
canSignIn: true,
canSignOut: false,
canAdjust: false
};
const actions = getRowActionButtons(uncheckRow);
assert.strictEqual(actions.signIn, true, '无签到记录学员签到按钮必须可见');
assert.strictEqual(actions.signOut, false, '未签到学员不能显示签退');
assert.strictEqual(actions.adjust, false, '无考勤记录学员不能显示调整');
console.log('✔ PASS: Unchecked student has visible signIn button, no duplicate path');
}
// B. 已有签到记录:签到按钮不可重复执行
{
const signedInRow = {
enrollmentId: 101,
checkinId: 8001,
attendanceStatus: 1,
signInTime: '2026-09-21 08:30:00',
signOutTime: null,
canSignIn: false,
canSignOut: true,
canAdjust: true
};
const actions = getRowActionButtons(signedInRow);
assert.strictEqual(actions.signIn, false, '已有合法签到记录学员不得显示签到按钮');
assert.strictEqual(actions.signOut, true, '已签到学员允许签退');
assert.strictEqual(actions.adjust, true, '已有打卡记录允许人工调整');
console.log('✔ PASS: Signed-in student cannot duplicate sign-in');
}
// 3. 模拟 signIn 闭环与名册状态刷新
{
// 初始名册
let roster = [
{
enrollmentId: 201,
studentName: '张三',
checkinId: null,
attendanceStatus: null,
signInTime: null,
signOutTime: null,
canSignIn: true,
canSignOut: false,
canAdjust: false
}
];
assert.strictEqual(roster[0].canSignIn, true);
assert.strictEqual(roster[0].attendanceStatus, null);
// 模拟执行 signIn 成功
function mockSignIn(enrollmentId, lessonId) {
return Promise.resolve({
checkinId: 5001,
enrollmentId: enrollmentId,
lessonId: lessonId,
attendanceStatus: 1,
signInTime: '2026-09-21 09:00:00',
signOutTime: null
});
}
// 模拟刷新 roster
function mockRefreshRoster(updatedRecord) {
roster = roster.map(item => {
if (item.enrollmentId === updatedRecord.enrollmentId) {
return {
...item,
checkinId: updatedRecord.checkinId,
attendanceStatus: updatedRecord.attendanceStatus,
signInTime: updatedRecord.signInTime,
signOutTime: updatedRecord.signOutTime,
canSignIn: false, // 已签到禁止再签到
canSignOut: true,
canAdjust: true
};
}
return item;
});
}
mockSignIn(201, 1001).then(res => {
mockRefreshRoster(res);
assert.strictEqual(roster[0].checkinId, 5001);
assert.strictEqual(roster[0].attendanceStatus, 1);
assert.strictEqual(roster[0].canSignIn, false, '刷新后签到按钮必须消失');
assert.strictEqual(roster[0].canSignOut, true, '刷新后签退按钮必须激活');
assert.strictEqual(roster[0].canAdjust, true, '刷新后调整按钮必须激活');
console.log('✔ PASS: signIn success refreshes roster to current attendance fact');
});
}
// 4. 后端错误真实展示,不得出现假成功
{
function handleSignInError(err) {
// 页面逻辑:
let displayedMsg = '';
let isSuccess = false;
if (err && (err.msg || err.message)) {
displayedMsg = err.msg || err.message;
} else {
displayedMsg = '签到操作失败';
}
return { displayedMsg, isSuccess };
}
const backendError = { code: 400, msg: '课次未关联有效的培训课程计划' };
const feedback = handleSignInError(backendError);
assert.strictEqual(feedback.isSuccess, false);
assert.strictEqual(feedback.displayedMsg, '课次未关联有效的培训课程计划');
assert.strictEqual(feedback.displayedMsg.includes('成功'), false);
console.log('✔ PASS: Backend error is truthfully reported without false success');
}
// 5. 权限可达性检查:train_admin 具备 checkin 权限与 list 权限
{
const trainAdminPerms = ['train:attendance:list', 'train:attendance:checkin', 'train:attendance:edit', 'train:attendance:export'];
const canAccessList = trainAdminPerms.includes('train:attendance:list');
const canPerformSignIn = trainAdminPerms.includes('train:attendance:checkin');
const canPerformAdjust = trainAdminPerms.includes('train:attendance:edit');
assert.strictEqual(canAccessList, true, 'train_admin 必须有查看考勤名册权限');
assert.strictEqual(canPerformSignIn, true, 'train_admin 必须有课次签到权限');
assert.strictEqual(canPerformAdjust, true, 'train_admin 必须有课次调整权限');
console.log('✔ PASS: train_admin has full permission reachability');
}
console.log('ALL FRONTEND ATTENDANCE SIGN-IN ENTRY TESTS PASSED!');