import { describe, expect, it } from 'vitest'; import { canPullAttendance, 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 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, }); }); });