Files
mgm-daylight-mcrm-mobile-ios/tests/todoMigration.test.mjs
T
2026-09-16 10:43:59 +08:00

99 lines
5.5 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {readFile} from 'node:fs/promises';
const source=await readFile(new URL('../salessupport/todoList/todoList.vue',import.meta.url),'utf8');
const script=source.match(/<script>([\s\S]*?)<\/script>/)[1]
.replace(/import[\s\S]*?from\s+['"][^'"]+['"];?[^\n]*/g,'').replace('export default','return');
const utility=await readFile(new URL('../common/normalizeApiKeys.js',import.meta.url),'utf8');
const {normalizeApiKeys}=await import('data:text/javascript;base64,'+Buffer.from(utility).toString('base64'));
function setup({list=async()=>({success:true,data:{records:[]}}),detail=async()=>({success:true,data:{}})}={}) {
const notices=[];
const page=new Function('mgmHeader','SimpleWatermark','uni','normalizeApiKeys','getTodoList','getTodoDetail','setTimeout',script)(
{},{},{showLoading(){},hideLoading(){},showToast:v=>notices.push(v)},normalizeApiKeys,list,detail,fn=>fn());
const vm={$t:k=>k,currentTask:null,todoListRequestSeq:0,filterType:'IM',currentIndex:0,
startDate:'2026-09-09',endDate:'2026-09-10',calendarSave:[],listData:[]};
for(const [k,fn] of Object.entries(page.methods)) vm[k]=fn.bind(vm);
for(const [k,fn] of Object.entries(page.computed)) Object.defineProperty(vm,k,{get:fn.bind(vm)});
vm.formatDate=v=>v; vm.checkCalendarSave=()=>{};
return {vm,page,notices};
}
const rows=id=>({success:true,data:{records:[{date:'2026-09-09',dateRecords:[{playerId:id,eta:'13:45'}]}]}});
test('older success and error cannot overwrite latest Todo response',async()=>{
for(const rejectOld of [false,true]) {
const pending=[];
const {vm}=setup({list:()=>new Promise((resolve,reject)=>pending.push({resolve,reject}))});
const old=vm.getTodoListByData(); const latest=vm.getTodoListByData();
pending[1].resolve(rows('new')); await latest;
if(rejectOld) pending[0].reject(new Error('old failure')); else pending[0].resolve(rows('old'));
await old;
assert.equal(vm.listData[0].dateRecords[0].playerId,'new');
assert.equal(vm.listData[0].dateRecords[0].eta,'13:45');
assert.equal(vm.showTodoListView,true);
}
});
test('empty success and incomplete error response have distinct messages',async()=>{
for(const [response,message] of [[{success:true,data:{records:[]}},'common.noRecord'],
[{success:false},'todo.returnError'],[{success:false,code:'NO RECORD'},'common.noRecord']]) {
const {vm}=setup({list:async()=>response}); await vm.getTodoListByData();
assert.equal(vm.dataNull,message); assert.equal(vm.showTodoListView,false);
}
});
test('native Error without errMsg is handled',async()=>{
const {vm}=setup({list:async()=>{throw new Error('network');}});
await vm.getTodoListByData(); assert.equal(vm.dataNull,'todo.returnError');
});
test('detail normalizes IRC balance and nested Offer fields in both casings',async()=>{
for(const data of [{IRCProgramFundBalance:'0',Descriptions:[{ItemRecordType:'Offer'}]},
{iRCProgramFundBalance:'0',descriptions:[{itemRecordType:'Offer'}]}]) {
const {vm}=setup({detail:async()=>({success:true,data})});
const record={playerId:'80001',actionDate:'2026-09-09',arrowName:'arrow-down'};
await vm.fetchTodoDetail(record);
assert.equal(record.detailData.iRCProgramFundBalance,'0');
assert.equal(record.showOffer,true); assert.equal(record.showDetail,true);
assert.equal(record.isProcessing,false);
}
});
test('ETA label still uses Daylight eta field',()=>{
const {vm,page}=setup();
assert.equal(page.computed.getETALable.call(vm)({eta:'13:45',eTA:'09:00'}),'13:45');
});
test('Todo formatters accept API string amounts, hours and ratios',()=>{
const {vm}=setup();
assert.equal(vm.formatNumber('3000000000'),'3,000,000,000');
assert.equal(vm.formatNumber('12.75'),'12.75');
assert.equal(vm.formatNumber('0'),'0');assert.equal(vm.formatNumber(null),'0');
assert.equal(vm.formarHour('1.5'),'1.5 hours');
assert.equal(vm.formarHour('0'),'0 hour');
assert.equal(vm.getPercentFormat('0.25'),'25%');
assert.equal(vm.getPercentFormat('0'),'0%');
});
test('Sharer renderer preserves trailing room status and notes and only renders Y icon',()=>{
const {vm}=setup();
for(const flag of ['Y','N']) {
const parts=vm.getRoomDescriptionParts({itemRecordType:'Room',itemRecordDescription:`Conf#123 [Rm Ready] | Suite | Sharer:${flag} | Note`});
assert.equal(parts.filter(p=>p.type==='sharerIcon').length,flag==='Y'?1:0);
const text=parts.filter(p=>p.type==='text').map(p=>p.text).join('');
assert.ok(text.includes('Conf#123 [Rm Ready]'));assert.ok(text.includes('Note'));
assert.ok(!text.includes('Sharer:'));
}
assert.equal(vm.getItemShowSharerIcon('Conf#123 | Sharer:N | Note'),false);
assert.deepEqual(vm.getDescriptionWithSharerIconParts(null),[]);
});
test('summary room markers use row flags only on first line and Limo warning excludes Inhouse',()=>{
const conditions=[...source.matchAll(/v-show="([^"]+)"/g)].map(m=>m[1]);
for(const field of ['isQueue','roomReady']) {
const expression=conditions.find(c=>c.includes(`rowRecord.${field}`));
assert.ok(expression);
const evaluate=new Function('index','rowRecord',`return ${expression}`);
assert.equal(evaluate(0,{[field]:true}),true);
assert.equal(evaluate(1,{[field]:true}),false);
assert.equal(evaluate(0,{[field]:false}),false);
}
const warning=conditions.find(c=>c.includes("hasLimoRecord =='-1'"));
const evaluate=new Function('index','currentIndex','rowRecord',`return ${warning}`);
assert.equal(evaluate(0,0,{hasLimoRecord:-1}),true);
assert.equal(evaluate(0,2,{hasLimoRecord:-1}),false);
assert.equal(evaluate(1,0,{hasLimoRecord:-1}),false);
assert.equal(evaluate(0,0,{hasLimoRecord:1}),false);
});