| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- 'use strict';
- const assert = require('node:assert/strict');
- const fs = require('node:fs');
- const path = require('node:path');
- const vm = require('node:vm');
- /** @param {string} source 页面源码 @param {string} name 方法名 @returns {string} 可执行方法 */
- function extract(source, name) {
- const match = new RegExp('export function ' + name + '\\([^]*?\\n}').exec(source);
- assert.ok(match, name);
- return match[0].replace('export ', '');
- }
- /** @param {string} file 页面文件 @param {boolean} employment 是否验证任职控制 @returns {object} 独立页面上下文 */
- function page(file, employment) {
- const source = fs.readFileSync(path.resolve(__dirname, '../src', file), 'utf8');
- const names = ['isWeekend', 'isDayOff', 'getHolidayType', 'isBeisenLeaveWorkday', 'loadHolidays'];
- if (employment) names.push('getEmploymentDateKey', 'getEmploymentBounds', 'isOutsideEmploymentDates',
- 'isBeforeEmploymentDate', 'assertEmploymentInputs', 'assertPersonnelSnapshot',
- 'getDayTotal', 'getOvertimeDayTotal', 'validateMonthlyOvertime', 'applyDefaultWorkdayHours');
- const scope = {APP_TYPE:'app', FORM_IDS:{holiday:'calendar'},
- HOLIDAY_FIELD_IDS:{year:'year',date:'date',type:'type'},
- VALIDATION:{maxHoursPerDay:8,maxOvertimePerMonth:36}};
- vm.createContext(scope);
- vm.runInContext(names.map(name => extract(source, name)).join('\n'), scope);
- const env = {...scope, state:{selectedYear:2026,selectedMonth:10,userInfo:{},
- billableData:{},nonBillableData:{},otherData:{},holidayMap:{1:'调休',10:'加班'}},
- getTexts:()=>({employmentInvalid:'invalid dates',employmentDraftOutside:'outside ',
- personnelCalendarChanged:'reload',overtimeExceeded:()=> 'over 36'}),
- utils:{toast:()=>{}},showTimecardError:()=>{},isNormalTimecardBlocked:()=>false};
- return env;
- }
- /** @returns {Promise<void>} 实际业务方法的日期、日历、默认值和提交前复核测试 */
- async function main() {
- const files = ['timecard-main.actions.js','timecard-repair.oyd.jsx','timecard-repair-lifecycle.oyd.jsx','timecard-change.oyd.jsx'];
- for (const file of files) {
- const employment = !file.includes('change');
- const env = page(file, employment);
- let queries = 0;
- env.utils.yida = {searchFormDatas:async () => { queries++; return {data:[]}; }};
- assert.equal(env.isDayOff(2026,10,1),true,'国内调休为休息日');
- assert.equal(env.isDayOff(2026,10,10),false,'国内周末补班为工作日');
- env.state.userInfo.isOverseasStaff = true;
- env._tcHolidayCalendarByYear = {2026:{20261001:'调休',20261010:'加班'}};
- assert.equal(env.isDayOff(2026,10,1),false);
- assert.equal(env.isDayOff(2026,10,10),true);
- assert.equal(env.isBeisenLeaveWorkday(2026,10,1),true);
- assert.equal(env.isBeisenLeaveWorkday(2026,10,10),false);
- assert.equal(env.getHolidayType(1),null);
- assert.equal(Object.keys(await env.loadHolidays(2026,10)).length,0);
- assert.equal(Object.keys(env._tcHolidayCalendarByYear).length,0);
- assert.equal(queries,0,'海外员工不请求国内日历');
- env.state.userInfo.isOverseasStaff = false;
- await env.loadHolidays(2026,10);
- assert.equal(queries,1,'切换回国内仍查询日历');
- if (!employment) continue;
- env.state.userInfo = {employmentStartDate:'2026-10-01',employmentEndDate:'2026-10-15',isOverseasStaff:true};
- assert.equal(env.isBeforeEmploymentDate(2026,9,30),true);
- assert.equal(env.isBeforeEmploymentDate(2026,10,1),false);
- assert.equal(env.isBeforeEmploymentDate(2026,10,15),false);
- assert.equal(env.isBeforeEmploymentDate(2026,10,16),true);
- assert.equal(env.getEmploymentDateKey(Date.parse('2026-10-14T16:00:00Z')),20261015);
- assert.equal(env.getEmploymentDateKey('2026-10-14T16:00:00Z'),20261015);
- assert.throws(()=> env.getEmploymentDateKey('2026-02-30'),/invalid/);
- assert.throws(()=> env.getEmploymentBounds({employmentStartDate:'2026-10-16',employmentEndDate:'2026-10-15'}),/invalid/);
- env.state.billableData = {0:{16:8}};
- assert.throws(()=> env.assertEmploymentInputs(),/20261016/,'范围外草稿不静默过滤');
- env.state.cellStatusMap = {billable_0_16:'approved'};
- env.assertEmploymentInputs();
- env.state.billableData = {};
- env.assertPersonnelSnapshot({...env.state.userInfo});
- assert.throws(()=> env.assertPersonnelSnapshot({...env.state.userInfo,isOverseasStaff:false}),/reload/);
- assert.throws(()=> env.assertPersonnelSnapshot({...env.state.userInfo,employmentEndDate:'2026-10-14'}),/reload/);
- const billable = {};
- env.applyDefaultWorkdayHours(2026,10,env.state.holidayMap,[{code:'P'}],billable,{}, {},false,{});
- assert.equal(billable[0][1],8,'海外节假日中的自然工作日默认8小时');
- assert.equal(billable[0][10],undefined,'海外周末补班不默认8小时');
- assert.equal(billable[0][16],undefined,'离职后不默认工时');
- env.state.userInfo = {isOverseasStaff:true};
- env.state.billableData = {0:{1:8,10:8}};
- assert.equal(env.validateMonthlyOvertime(),true);
- for (const hours of [36,36.5]) {
- env.state.billableData = {0:{3:hours/2,4:hours/2}};
- assert.equal(env.validateMonthlyOvertime(),hours===36,'保留36小时边界');
- }
- env.state.isExternalStaff = true;
- assert.equal(env.validateMonthlyOvertime(),true,'保留外部员工原豁免');
- }
- console.log('PASS 四入口海外自然周、国内回退、任职闭区间、上海时区、默认值、草稿保护、档案变更及36小时边界');
- }
- main().catch(error => {console.error(error);process.exitCode=1;});
|