financial_system/frontend/tests/payrollCenterFlow.test.ts
2026-06-29 14:59:49 +08:00

202 lines
9.6 KiB
TypeScript

declare const process: { cwd(): string };
declare function require(name: string): {
existsSync?: (path: string) => boolean;
readFileSync?: (path: string, encoding: string) => string;
join?: (...parts: string[]) => string;
};
const { existsSync, readFileSync } = require("node:fs");
const { join } = require("node:path");
if (!existsSync || !readFileSync || !join) {
throw new Error("测试运行环境缺少文件读取能力");
}
const root = process.cwd();
const realtimeApiPath = join(root, "src/api/realtimeAttendance.ts");
const monthlyApiPath = join(root, "src/api/monthlyPayroll.ts");
const realtimeViewPath = join(root, "src/views/RealtimeAttendanceView.vue");
const monthlyViewPath = join(root, "src/views/MonthlyPayrollView.vue");
const resultsTablePath = join(root, "src/components/ResultsTable.vue");
const topBarPath = join(root, "src/components/TopBar.vue");
const dashboardViewPath = join(root, "src/views/DashboardView.vue");
const routerPath = join(root, "src/router/index.ts");
assertTruthy(existsSync(realtimeApiPath), "缺少实时考勤 API 客户端");
assertTruthy(existsSync(monthlyApiPath), "缺少月度核算 API 客户端");
assertTruthy(existsSync(resultsTablePath), "缺少工资明细表组件");
assertTruthy(existsSync(topBarPath), "缺少顶部导航组件");
const realtimeApi = readFileSync(realtimeApiPath, "utf-8");
assertIncludes(realtimeApi, "/api/attendance/realtime/today");
assertIncludes(realtimeApi, "/api/attendance/realtime/sync");
const monthlyApi = readFileSync(monthlyApiPath, "utf-8");
assertIncludes(monthlyApi, "/api/payroll/monthly/runs");
assertIncludes(monthlyApi, "/api/payroll/monthly/dingtalk");
assertIncludes(monthlyApi, "/api/payroll/monthly/excel");
const realtimeView = readFileSync(realtimeViewPath, "utf-8");
assertNotIncludes(realtimeView, "待接入菜单");
assertNotIncludes(realtimeView, "待接入钉钉同步");
assertIncludes(realtimeView, "预估金额,最终以月度核算锁定结果为准");
assertIncludes(realtimeView, "realtime-page-header");
assertIncludes(realtimeView, "realtime-header-actions");
assertIncludes(realtimeView, "realtime-date-field");
assertIncludes(realtimeView, "realtime-overview-panel");
assertIncludes(realtimeView, "realtime-overview-header");
assertIncludes(realtimeView, "realtime-overview-title");
assertIncludes(realtimeView, "realtime-overview-stats");
assertIncludes(realtimeView, "syncStatusText");
assertIncludes(realtimeView, "formatSyncTime");
assertIncludes(realtimeView, "realtime-sync-chip");
assertNotIncludes(realtimeView, 'response?.sync_job_id ? "已同步" : "未同步"');
assertIncludes(realtimeView, "employeeQuery");
assertIncludes(realtimeView, "departmentFilter");
assertIncludes(realtimeView, "departmentOptions");
assertIncludes(realtimeView, "filteredRows");
assertIncludes(realtimeView, "hasRealtimeFilters");
assertIncludes(realtimeView, "clearRealtimeFilters");
assertIncludes(realtimeView, "realtime-table-filter-bar");
assertIncludes(realtimeView, "部门筛选");
assertIncludes(realtimeView, "员工搜索");
assertIncludes(realtimeView, "realtime-clear-filter");
assertIncludes(realtimeView, ":disabled=\"!hasRealtimeFilters\"");
assertIncludes(realtimeView, "@click=\"clearRealtimeFilters\"");
assertIncludes(realtimeView, "清空");
assertIncludes(realtimeView, "显示 {{ filteredRows.length }} / {{ rows.length }} 人");
assertIncludes(realtimeView, "v-for=\"row in filteredRows\"");
const topBar = readFileSync(topBarPath, "utf-8");
assertIncludes(topBar, "useRoute");
assertIncludes(topBar, "showGlobalSearch");
assertIncludes(topBar, 'route.path !== "/attendance/realtime"');
assertIncludes(topBar, 'v-if="showGlobalSearch"');
const monthlyView = readFileSync(monthlyViewPath, "utf-8");
assertNotIncludes(monthlyView, "下一阶段接入");
assertNotIncludes(monthlyView, "核算流程");
assertNotIncludes(monthlyView, "计算编号仅用于系统排查");
assertIncludes(monthlyView, "本月核算");
assertNotIncludes(monthlyView, "工资核算工作台");
assertIncludes(monthlyView, "同步钉钉核算");
assertIncludes(monthlyView, "导入 Excel 核算");
assertNotIncludes(monthlyView, "payroll-workbench");
assertNotIncludes(monthlyView, "payroll-flow-strip");
assertIncludes(monthlyView, "payroll-compact-toolbar");
assertIncludes(monthlyView, "payroll-toolbar-month");
assertIncludes(monthlyView, "payroll-toolbar-summary");
assertIncludes(monthlyView, "payroll-toolbar-actions");
assertIncludes(monthlyView, "payroll-toolbar-export");
assertIncludes(monthlyView, "payroll-toolbar-range");
assertIncludes(monthlyView, "payroll-detail-panel");
assertIncludes(monthlyView, "payroll-detail-summary");
assertIncludes(monthlyView, "payroll-secondary-grid");
assertIncludes(monthlyView, ":exceptions=\"detail.exceptions\"");
assertNotIncludes(monthlyView, "payroll-command-copy");
assertNotIncludes(monthlyView, "payroll-command-card");
assertNotIncludes(monthlyView, "payroll-command-step");
assertNotIncludes(monthlyView, "payroll-step-index");
assertNotIncludes(monthlyView, "payroll-period-row");
assertNotIncludes(monthlyView, "payroll-source-row");
assertNotIncludes(monthlyView, "payroll-source-copy");
assertNotIncludes(monthlyView, "payroll-summary-band");
assertNotIncludes(monthlyView, "核算操作");
assertNotIncludes(monthlyView, "1 选择月份");
assertNotIncludes(monthlyView, "2 取数核算");
assertNotIncludes(monthlyView, "3 核对工资");
assertNotIncludes(monthlyView, "4 锁定导出");
assertNotIncludes(monthlyView, "payroll-action-card");
assertNotIncludes(monthlyView, "payroll-result-panel");
assertIncludes(monthlyView, "锁定后本月工资不能重新计算");
assertIncludes(monthlyView, "核算历史");
assertIncludes(monthlyView, "查看明细");
assertIncludes(monthlyView, "技术信息");
assertIncludes(monthlyView, "<summary>技术信息</summary>");
assertNotIncludes(monthlyView, "请输入任务编号");
assertIncludes(monthlyView, "calculateMonthlyFromDingTalk");
assertIncludes(monthlyView, "calculateMonthlyFromExcel");
assertIncludes(monthlyView, "monthly_payroll:calculate");
assertNotIncludes(monthlyView, "monthly-source-strip");
assertIncludes(monthlyView, "核算月份");
assertIncludes(monthlyView, "同步范围");
assertIncludes(monthlyView, "Excel 文件月份");
assertIncludes(monthlyView, "请选择核算月份后再同步钉钉");
assertIncludes(monthlyView, "已根据文件识别核算月份");
assertIncludes(monthlyView, "生成 Excel 结果文件");
assertNotIncludes(monthlyView, "优先同步钉钉,也可导入钉钉月度汇总 Excel。");
assertNotIncludes(monthlyView, "选择 Excel 后会自动识别文件月份。");
assertNotIncludes(monthlyView, ">01<");
assertNotIncludes(monthlyView, ">02<");
assertBefore(monthlyView, "核算月份", "同步钉钉核算");
assertBefore(monthlyView, "工资明细", "核算历史");
assertBefore(monthlyView, "工资明细", "异常清单");
assertBefore(monthlyView, "payroll-compact-toolbar", "payroll-detail-panel");
assertBefore(monthlyView, "payroll-detail-panel", "payroll-secondary-grid");
const resultsTable = readFileSync(resultsTablePath, "utf-8");
assertIncludes(resultsTable, "embedded");
assertIncludes(resultsTable, "salary-detail-table");
assertIncludes(resultsTable, "salary-summary-toolbar");
assertIncludes(resultsTable, "salary-summary-table");
assertIncludes(resultsTable, "salary-detail-drawer");
assertIncludes(resultsTable, "filteredResults");
assertIncludes(resultsTable, "anomalyLabel");
assertIncludes(resultsTable, "查看构成");
assertIncludes(resultsTable, "部门");
assertIncludes(resultsTable, "异常");
assertIncludes(resultsTable, "实发工资");
assertBefore(resultsTable, "salary-summary-table", "salary-detail-drawer");
const dashboardView = readFileSync(dashboardViewPath, "utf-8");
assertNotIncludes(dashboardView, "/payroll/jobs");
assertNotIncludes(dashboardView, 'to="/payroll"');
assertNotIncludes(dashboardView, "readRecentJobs");
assertNotIncludes(dashboardView, "RecentPayrollJob");
assertIncludes(dashboardView, "listMonthlyRuns");
assertIncludes(dashboardView, "exportMonthlyRun");
assertNotIncludes(dashboardView, "payroll:dingtalk:calculate");
assertIncludes(dashboardView, "monthly_payroll:calculate");
assertNotIncludes(dashboardView, "任务编号");
assertNotIncludes(dashboardView, "任务记录");
assertNotIncludes(dashboardView, "最近任务");
assertNotIncludes(dashboardView, "任务来源");
assertNotIncludes(dashboardView, "暂无计算记录");
assertIncludes(dashboardView, "核算历史");
assertIncludes(dashboardView, "最近核算人数");
assertIncludes(dashboardView, "工资月份");
assertIncludes(dashboardView, "实发合计");
const router = readFileSync(routerPath, "utf-8");
assertIncludes(router, 'path: "payroll"');
assertIncludes(router, 'redirect: "/payroll/monthly"');
assertNotIncludes(router, "../views/PayrollView.vue");
assertNotIncludes(router, "component: PayrollView");
assertNotIncludes(router, 'meta: { permission: "payroll:excel:calculate" }');
function assertIncludes(content: string, expected: string): void {
if (!content.includes(expected)) {
throw new Error(`期望包含 ${expected}`);
}
}
function assertNotIncludes(content: string, unexpected: string): void {
if (content.includes(unexpected)) {
throw new Error(`不应包含 ${unexpected}`);
}
}
function assertTruthy(value: boolean, message: string): void {
if (!value) {
throw new Error(message);
}
}
function assertBefore(content: string, first: string, second: string): void {
const firstIndex = content.indexOf(first);
const secondIndex = content.indexOf(second);
if (firstIndex === -1 || secondIndex === -1 || firstIndex >= secondIndex) {
throw new Error(`期望 ${first} 出现在 ${second} 之前`);
}
}