Files
gongxue-base/apps/admin/src/pages/Attendance/attendance-workspace.integration.test.ts

122 lines
3.9 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,
});
});
});