const fs=require('node:fs');
|
const path=require('node:path');
|
const assert=require('node:assert/strict');
|
const front=path.resolve(__dirname,'../../mom-pro2-before');
|
const vue=require(require.resolve('vue',{paths:[front]}));
|
const sfc=require(require.resolve('vue/compiler-sfc',{paths:[front]}));
|
const ts=require(require.resolve('typescript',{paths:[front]}));
|
const folder=path.join(front,'src/views/crm/backlog');
|
const row={id:11,customerId:22,businessId:33,signContactId:44,orderId:55,contractId:66,invoiceId:77};
|
|
function load(file,{failedCount}={}){
|
const source=fs.readFileSync(file,'utf8');const parsed=sfc.parse(source,{filename:file});
|
assert.equal(parsed.errors.length,0);
|
assert.equal(sfc.compileTemplate({source:parsed.descriptor.template.content,filename:file,id:file}).errors.length,0);
|
const compiled=sfc.compileScript(parsed.descriptor,{id:file});
|
const js=ts.transpileModule(compiled.content,{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2020}}).outputText;
|
const state={calls:[],routes:[],events:[],queries:0,opens:0,modalData:null,grid:null};
|
const stub={name:'Stub',setup(){return()=>null;}};
|
const policy=vue.ref(false);
|
function req(name){return async (...args)=>{state.calls.push({name,args});if(name===failedCount)throw Error('simulated API failure');return name.endsWith('Count')?7:{list:[],total:0};};}
|
function mock(name){
|
if(name==='vue')return {...vue,onMounted(){},onActivated(){}};
|
if(name==='vue-router')return {useRouter:()=>({push:(r)=>state.routes.push(r)})};
|
if(name==='ant-design-vue')return {Button:stub,Tag:stub,Badge:stub,Card:stub,List:stub,message:{loading:()=>()=>{},success(){}}};
|
if(name==='@vben/common-ui')return {Page:stub,useVbenModal:()=>[stub,{setData(data){state.modalData=data;return this;},open(){state.opens++;}}]};
|
if(name==='@vben/utils')return {erpNumberFormatter:(x)=>String(x)};
|
if(name==='#/store/audit-policy')return {automaticApproval:policy};
|
if(name==='#/adapter/vxe-table')return {TableAction:stub,ACTION_ICON:{VIEW:'view'},useVbenVxeGrid:(options)=>{state.grid=options;return[stub,{query:async()=>{state.queries++;}}];}};
|
if(name.includes('/api/'))return new Proxy({},{get:(_,key)=>req(key)});
|
if(name.includes('/views/'))return {useGridColumns:()=>[]};
|
if(name.endsWith('.vue'))return {default:stub};
|
if(name==='../data')return {AUDIT_STATUS:[{value:10}],CONTRACT_EXPIRY_TYPE:[{value:1}],RECEIVABLE_REMIND_TYPE:[{value:1}],CONTACT_STATUS:[{value:1}],SCENE_TYPES:[{value:1}],FOLLOWUP_STATUS:[]};
|
if(name==='./data')return {useLeftSides:()=>[]};
|
throw Error('Missing mock '+name);
|
}
|
const module={exports:{}};new Function('require','module','exports',js)(mock,module,module.exports);
|
const vm=module.exports.default.setup({}, {expose(){},emit:(...args)=>state.events.push(args)});
|
return {vm,state,descriptor:parsed.descriptor,policy};
|
}
|
|
async function main(){
|
let checked=0;
|
for(const name of fs.readdirSync(path.join(folder,'modules'))){
|
if(!name.endsWith('.vue'))continue;
|
const file=path.join(folder,'modules',name),{vm,state,descriptor}=load(file);
|
const imported=fs.readFileSync(file,'utf8').match(/import \{ useGridColumns \} from '#\/views\/([^']+)'/);
|
assert(imported,name+' schema import');
|
const schema=path.join(front,'src/views',imported[1]+'.ts');
|
const ast=ts.createSourceFile(schema,fs.readFileSync(schema,'utf8'),ts.ScriptTarget.Latest,true);
|
const fn=ast.statements.find(n=>ts.isFunctionDeclaration(n)&&n.name?.text==='useGridColumns');assert(fn);
|
const slots=[...fn.getText(ast).matchAll(/default:\s*'([^']+)'/g)].map(x=>x[1]);
|
for(const slot of slots)assert(descriptor.template.content.includes('#'+slot+'='),name+' missing column slot '+slot);
|
const values=Object.fromEntries(state.grid.formOptions.schema.filter(x=>x.defaultValue!==undefined).map(x=>[x.fieldName,x.defaultValue]));
|
await state.grid.gridOptions.proxyConfig.ajax.query({page:{currentPage:2,pageSize:20}},values);
|
const call=state.calls.at(-1);assert.equal(call.args[0].pageNo,2);assert.equal(call.args[0].pageSize,20);
|
if(name.startsWith('contract-')){
|
const checks=[['handleContractDetail','CrmContractDetail','id'],['handleCustomerDetail','CrmCustomerDetail','customerId'],['handleBusinessDetail','CrmBusinessDetail','businessId'],['handleContactDetail','CrmContactDetail','signContactId']];
|
for(const [handler,route,key] of checks){vm[handler](row);assert.deepEqual(state.routes.at(-1),{name:route,params:{id:row[key]}});}
|
const count=state.routes.length;vm.handleCustomerDetail({});vm.handleBusinessDetail({});vm.handleContactDetail({});assert.equal(state.routes.length,count);
|
vm.handleViewOrder(row);assert.deepEqual(state.routes.at(-1),{name:'ErpSaleOrder',query:{id:55}});
|
}
|
if(name==='customer-put-pool-remind-list.vue'){assert.equal(call.name,'getPutPoolRemindCustomerPage');assert(!('pool' in call.args[0]));}
|
if(name==='receivable-plan-remind-list.vue'){
|
assert.equal(call.args[0].sceneType,1);vm.handleDetail(row);assert.deepEqual(state.routes.at(-1),{name:'CrmReceivablePlanDetail',params:{id:11}});
|
vm.handleCreateReceivable({...row,receivableId:123});assert.equal(state.opens,0);
|
vm.handleCreateReceivable(row);assert.equal(state.opens,1);assert.equal(state.modalData.plan,row);
|
vm.handleRefresh();assert.equal(state.queries,1);assert.deepEqual(state.events,[['refresh']]);
|
}
|
if(name==='receivable-audit-list.vue'){
|
assert.equal(call.args[0].sceneType,1);vm.handleContractDetail(row);assert.equal(state.routes.at(-1).params.id,66);
|
vm.handleInvoiceDetail(row);assert.equal(state.routes.at(-1).params.id,77);
|
await vm.handleAudit(row);assert(state.calls.some(x=>x.name==='updateReceivableStatus'&&x.args[0]===11&&x.args[1]===20));
|
assert.equal(state.queries,1);assert.deepEqual(state.events,[['refresh']]);
|
}
|
checked++;
|
}
|
const {vm,state}=load(path.join(folder,'index.vue'),{failedCount:'getTodayContactCustomerCount'});
|
const first=vm.getCount();assert.equal(vm.getCount(),first,'Overlapping mount/activation requests should be deduplicated');await first;
|
assert.equal(state.calls.length,8);assert.equal(vm.customerTodayContactCount.value,0);assert.equal(vm.contractRemindCount.value,7);assert.equal(vm.receivablePlanRemindCount.value,7);
|
await vm.getCount();assert.equal(state.calls.length,16,'Refresh should request fresh counts');
|
const result={passed:true,module_components:checked,vue_compilation:true,all_shared_column_slots_present:true,related_entity_routes:true,pool_reminder_endpoint:true,receivable_plan_route_and_repeat_guard:true,mutation_refresh_events:true,counter_partial_failure_isolation:true,counter_request_deduplication:true};
|
fs.writeFileSync(path.join(__dirname,'verification.json'),JSON.stringify(result,null,2));console.log(JSON.stringify(result));
|
}
|
main().catch(e=>{console.error(e);process.exitCode=1;});
|