forked from jiaoly/financial_system
202 lines
9.6 KiB
TypeScript
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} 之前`);
|
|
}
|
|
}
|