Files
gongxue-base/apps/admin/src/pages/Attendance/attendance-workspace.integration.test.ts
wangziqi 375c7ec60b feat: refine admin forms, attendance and finance workflows
Squash merge PR #23.

Included changes:
- complete occupancy check-in required fields/default payload
- improve responsive admin management pages
- fix attendance edge cases and attendance period config
- refine wallet/finance-related workflow handling

Checks:
- npm run typecheck -w apps/admin
- npm run typecheck -w apps/server
2026-07-18 12:54:10 +00:00

127 lines
4.0 KiB
TypeScript
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.

import { describe, expect, it } from 'vitest';
import {
canPullAttendance,
filterLessonAttendanceRecords,
getAttendanceExperience,
getPunchDisplayInfo,
getSchedulePhase,
summarizeAttendance,
summarizeLessonCheckins,
} from './attendance-workspace';
describe('attendance role experience', () => {
it('routes class-scoped teachers to the teaching workspace', () => {
expect(
getAttendanceExperience(
['attendance:view', 'attendance:create', 'schedule:create'],
['老师'],
),
).toBe('teacher');
});
it('routes users with attendance administration permission to history management', () => {
expect(getAttendanceExperience(['attendance:view', 'attendance:edit'], ['教务管理员'])).toBe(
'admin',
);
});
});
describe('teacher schedule phase', () => {
it('marks a finished lesson as ready for attendance review', () => {
expect(getSchedulePhase('08:00', '09:00', new Date('2026-07-11T10:00:00'))).toBe('ended');
});
it('keeps future lessons read-only', () => {
expect(getSchedulePhase('14:00', '15:00', new Date('2026-07-11T10:00:00'))).toBe('upcoming');
});
it('allows attendance pulls once the lesson starts', () => {
expect(canPullAttendance('ongoing')).toBe(true);
expect(canPullAttendance('ended')).toBe(true);
expect(canPullAttendance('upcoming')).toBe(false);
});
});
describe('attendance summary', () => {
it('summarizes status counts for an administrator history view', () => {
expect(
summarizeAttendance([
{ status: 'present' },
{ status: 'present' },
{ status: 'late' },
{ status: 'absent' },
]),
).toEqual({ total: 4, present: 2, late: 1, absent: 1, leave: 0, pending: 0 });
});
});
describe('lesson check-in summary', () => {
it('counts late punches as checked in and missing punches as not checked in', () => {
expect(
summarizeLessonCheckins([
{ status: 'present' },
{ status: 'late' },
{ status: 'pending' },
{ status: 'absent' },
]),
).toEqual({ total: 4, checkedIn: 2, notCheckedIn: 2 });
});
});
describe('lesson attendance filters', () => {
const records = [
{ id: 1, student: { name: '张三' }, status: 'present' },
{ id: 2, student: { name: '李四' }, status: 'late' },
{ id: 3, student: { name: '王五' }, status: 'pending' },
{ id: 4, student: { name: '赵六' }, status: 'absent' },
];
it('searches students by name and ignores surrounding whitespace', () => {
expect(filterLessonAttendanceRecords(records, ' 张 ', 'all').map((item) => item.id)).toEqual([
1,
]);
});
it('groups present and late as checked in', () => {
expect(filterLessonAttendanceRecords(records, '', 'checked_in').map((item) => item.id)).toEqual(
[1, 2],
);
});
it('groups pending and absent as not checked in and combines with search', () => {
expect(
filterLessonAttendanceRecords(records, '王', 'not_checked_in').map((item) => item.id),
).toEqual([3]);
});
});
describe('lesson punch device display', () => {
it('labels attendance machine punches with the machine name and id', () => {
expect(
getPunchDisplayInfo({
status: 'present',
source: 'dingtalk',
punchSource: 'ATM',
punchDeviceName: '东门考勤机',
punchDeviceId: 'ATM-01',
punchTime: '2026-07-11T00:55:00.000Z',
}),
).toEqual({
label: '考勤机打卡',
machine: true,
detail: '东门考勤机ATM-01',
time: '2026-07-11T00:55:00.000Z',
});
});
it('distinguishes mobile punches and manual teacher markings', () => {
expect(
getPunchDisplayInfo({ status: 'present', source: 'dingtalk', punchSource: 'USER' }),
).toEqual({ label: '手机打卡', machine: false, detail: undefined, time: undefined });
expect(getPunchDisplayInfo({ status: 'present', source: 'manual' })).toEqual({
label: '老师手动标记',
machine: false,
});
});
});