关键改动: - 成员管理表格改为卡片内滚动,并固定每个表头单元格 - 加班导出按当前月份筛选导出记录 - 增加成员表头与加班导出月份过滤测试 Co-Authored-By: Codex GPT-5 <codex@openai.com>
135 lines
3.6 KiB
TypeScript
135 lines
3.6 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import {
|
|
filterOvertimeRecordsForExportMonth,
|
|
filterOvertimeRecordsForViewer,
|
|
type OvertimeRecord,
|
|
} from './overtime';
|
|
import type { AuthUser } from './auth-user';
|
|
import type { Department, Member, RoleItem } from './members';
|
|
|
|
const departments: Department[] = [
|
|
{ id: 'dept-tech', name: 'Tech', order: 1, createdAt: '2024-01-01' },
|
|
{ id: 'dept-front', name: 'Frontend', parentId: 'dept-tech', order: 1, createdAt: '2024-01-01' },
|
|
{ id: 'dept-back', name: 'Backend', parentId: 'dept-tech', order: 2, createdAt: '2024-01-01' },
|
|
{ id: 'dept-product', name: 'Product', order: 2, createdAt: '2024-01-01' },
|
|
];
|
|
|
|
const members: Member[] = [
|
|
member('m-lead', 'Lead', 'dept-tech', 'role-lead'),
|
|
member('m-front', 'Frontend Alice', 'dept-front', 'role-dev'),
|
|
member('m-back', 'Backend Bob', 'dept-back', 'role-dev'),
|
|
member('m-pm', 'PM Carol', 'dept-product', 'role-pm'),
|
|
];
|
|
|
|
const records: OvertimeRecord[] = [
|
|
record('ot-front', 'Frontend Alice'),
|
|
record('ot-back', 'Backend Bob'),
|
|
record('ot-pm', 'PM Carol'),
|
|
];
|
|
|
|
const exportRecords: OvertimeRecord[] = [
|
|
{
|
|
id: 'july-1',
|
|
projectId: 'project-1',
|
|
person: 'Alice',
|
|
startTime: '2026-07-01T19:00:00',
|
|
endTime: '2026-07-01T21:00:00',
|
|
duration: 2,
|
|
reasonId: 'reason-1',
|
|
createdAt: '2026-07-01',
|
|
},
|
|
{
|
|
id: 'august-1',
|
|
projectId: 'project-1',
|
|
person: 'Bob',
|
|
startTime: '2026-08-02T19:00:00',
|
|
endTime: '2026-08-02T21:00:00',
|
|
duration: 2,
|
|
reasonId: 'reason-1',
|
|
createdAt: '2026-08-02',
|
|
},
|
|
];
|
|
|
|
test('filterOvertimeRecordsForViewer limits non-admin viewers to their department tree', () => {
|
|
const visible = filterOvertimeRecordsForViewer(records, {
|
|
viewer: toAuthUser(members[0]),
|
|
viewerRole: role('role-lead', ['overtime:view']),
|
|
members,
|
|
departments,
|
|
});
|
|
|
|
assert.deepEqual(visible.map((item) => item.id), ['ot-front', 'ot-back']);
|
|
});
|
|
|
|
test('filterOvertimeRecordsForViewer lets wildcard admin view every department', () => {
|
|
const visible = filterOvertimeRecordsForViewer(records, {
|
|
viewer: toAuthUser(members[0]),
|
|
viewerRole: role('role-admin', ['*']),
|
|
members,
|
|
departments,
|
|
});
|
|
|
|
assert.deepEqual(visible.map((item) => item.id), ['ot-front', 'ot-back', 'ot-pm']);
|
|
});
|
|
|
|
test('filters export records by selected month', () => {
|
|
const result = filterOvertimeRecordsForExportMonth(exportRecords, '2026-07');
|
|
|
|
assert.deepEqual(result.map((record) => record.id), ['july-1']);
|
|
});
|
|
|
|
test('keeps all export records when all months are selected', () => {
|
|
const result = filterOvertimeRecordsForExportMonth(exportRecords, '');
|
|
|
|
assert.deepEqual(result.map((record) => record.id), ['july-1', 'august-1']);
|
|
});
|
|
|
|
function member(id: string, name: string, departmentId: string, roleId: string): Member {
|
|
return {
|
|
id,
|
|
name,
|
|
username: id.toUpperCase(),
|
|
departmentId,
|
|
roleId,
|
|
phone: '',
|
|
email: '',
|
|
password: '',
|
|
createdAt: '2024-01-01',
|
|
};
|
|
}
|
|
|
|
function record(id: string, person: string): OvertimeRecord {
|
|
return {
|
|
id,
|
|
person,
|
|
projectId: 'project-1',
|
|
startTime: '2026-06-01T19:00',
|
|
endTime: '2026-06-01T21:00',
|
|
duration: 2,
|
|
reasonId: 'reason-1',
|
|
createdAt: '2026-06-01',
|
|
};
|
|
}
|
|
|
|
function role(id: string, permissions: string[]): RoleItem {
|
|
return {
|
|
id,
|
|
name: id,
|
|
createdAt: '2024-01-01',
|
|
permissions,
|
|
};
|
|
}
|
|
|
|
function toAuthUser(member: Member): AuthUser {
|
|
return {
|
|
id: member.id,
|
|
name: member.name,
|
|
username: member.username ?? member.id.toUpperCase(),
|
|
roleId: member.roleId,
|
|
departmentId: member.departmentId,
|
|
email: member.email,
|
|
};
|
|
}
|