fix bugs
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {readFile} from 'node:fs/promises';
|
||||
const source=await readFile(new URL('../booking/bookingRecord/bookingRecord.vue',import.meta.url),'utf8');
|
||||
function computed(name,next) {
|
||||
const start=source.indexOf(name+'(){');
|
||||
const end=source.indexOf(next+'()',start);
|
||||
assert.ok(start>=0 && end>start);
|
||||
return new Function(`return ({${source.slice(start,end).trim().replace(/,$/,'')}}).${name}();`)();
|
||||
}
|
||||
const color=computed('getRecordColor','getETA');
|
||||
const confirmation=computed('getConfirmationNo','dateFormat');
|
||||
test('AP retains approval color when lodging status also matches an ordinary booking',()=>{
|
||||
for(const lastOperaStatus of ['CHECKED_IN','DUE_OUT','RESERVED',null]) {
|
||||
for(const sharer of [null,'[]','']) {
|
||||
assert.equal(color({approveStatus:'AP',lastOperaStatus,confirmationNo:'12345',sharer}),'hz-card-approval');
|
||||
}
|
||||
}
|
||||
});
|
||||
test('waitlist and ordinary colors are preserved',()=>{
|
||||
for(const approveStatus of ['W','WA']) assert.equal(color({approveStatus,lastOperaStatus:'CHECKED_IN',confirmationNo:'12345'}),'hz-card-waitlist');
|
||||
assert.equal(color({approveStatus:'N',lastOperaStatus:'CHECKED_IN',confirmationNo:'12345'}),'hz-card-booking');
|
||||
});
|
||||
test('Daylight still displays an existing AP confirmation number',()=>{
|
||||
assert.equal(confirmation({approveStatus:'AP',confirmationNo:'12345'}),'12345');
|
||||
assert.equal(confirmation({approveStatus:'AP',confirmationNo:null}),'N/A');
|
||||
assert.equal(confirmation({approveStatus:'W',confirmationNo:'12345'}),'N/A');
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {readFile} from 'node:fs/promises';
|
||||
const source=await readFile(new URL('../booking/newBooking/newBooking.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');
|
||||
function setup() {
|
||||
let stored,scheduled,delay;
|
||||
const calls=[];
|
||||
const page=new Function('uni','playerLookup','setTimeout','clearTimeout',script)(
|
||||
{getStorageSync:()=>structuredClone(stored),setStorageSync:(k,v)=>stored=structuredClone(v),
|
||||
showLoading(){},hideLoading(){},$u:{toast(){}}},
|
||||
async params=>{calls.push(params);return {success:true,data:{englishName:'Original Name',tierName:'Gold'}};},
|
||||
(fn,ms)=>{scheduled=fn;delay=ms;return 1;},()=>{});
|
||||
const vm={modelBooking:{sharerList:[{playerId:'80001',name:'',tierName:''}]},
|
||||
playerData:{playerId:'90001'},userData:{userId:'1'},$t:k=>k,currentTask:null,getPlayerTimer:null};
|
||||
for(const [k,fn] of Object.entries(page.methods))vm[k]=fn.bind(vm);
|
||||
return {vm,calls,run:()=>scheduled(),delay:()=>delay};
|
||||
}
|
||||
test('Daylight delayed lookup and tier fill survive; editing resolved name clears only player ID',async()=>{
|
||||
const {vm,calls,run,delay}=setup();
|
||||
await vm.getPlayer(0);assert.equal(delay(),800);assert.equal(calls.length,0);
|
||||
await run();assert.equal(calls[0].playerId,'80001');
|
||||
const item=vm.modelBooking.sharerList[0];
|
||||
assert.equal(item.name,'Original Name');assert.equal(item.tierName,'Gold');
|
||||
await vm.getPlayerName(0);assert.equal(item.playerId,'80001');
|
||||
item.name='Edited Name';await vm.getPlayerName(0);
|
||||
assert.equal(item.playerId,'');assert.equal(item.name,'Edited Name');
|
||||
});
|
||||
test('source length threshold is retained for partial IDs',async()=>{
|
||||
const {vm,run}=setup();await vm.getPlayer(0);await run();
|
||||
const item=vm.modelBooking.sharerList[0];item.name='Edited Name';item.playerId='1234';
|
||||
await vm.getPlayerName(0);assert.equal(item.playerId,'1234');
|
||||
item.playerId='12345';await vm.getPlayerName(0);assert.equal(item.playerId,'');
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {readFile} from 'node:fs/promises';
|
||||
const source=await readFile(new URL('../booking/submitBooking/submitBooking.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');
|
||||
function setup(bookType=0) {
|
||||
const saved=[],logs=[];
|
||||
const page=new Function('uni','saveBooking','getApprover',script)(
|
||||
{showLoading(){},hideLoading(){},showToast(){},$u:{throttle(){}}},
|
||||
async data=>{saved.push(structuredClone(data));return {success:true};},
|
||||
async()=>({success:true,data:{records:[{approverList:[{employeeId:'7',employeeName:'Approver'}],approvalRoutingMessage:'Route'}]}}));
|
||||
const vm={$t:k=>k,$set:(o,k,v)=>o[k]=v,writeLog:async(...args)=>logs.push(args)};
|
||||
Object.assign(vm,page.data.call(vm));
|
||||
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.bookingData={bookType,dateList:[{date:['2026-09-09','2026-09-10'],status:1}],
|
||||
extendDate:[{date:['2026-09-10','2026-09-11'],status:1}],roomRemark:'',otherRemark:''};
|
||||
vm.checkList=[0]; vm.playerData={playerId:'80001'};vm.userData={userId:'1'};
|
||||
return {vm,saved,logs};
|
||||
}
|
||||
test('direct Waitlist initializes selected dates for normal and extension booking',()=>{
|
||||
for(const type of [0,1]) {
|
||||
const {vm}=setup(type); vm.showWaitlistPop();
|
||||
assert.equal(vm.unqualifiedDateList[0],type?vm.bookingData.extendDate[0]:vm.bookingData.dateList[0]);
|
||||
assert.deepEqual(vm.unqualifiedDateCheckboxStates,[[]]);assert.equal(vm.showWaitlistModal,true);
|
||||
}
|
||||
});
|
||||
test('Waitlist room choice is mutually exclusive and can be deselected',()=>{
|
||||
const {vm}=setup();
|
||||
vm.onWaitlistRoomRemarkGroupChange(['2nd Room']);
|
||||
assert.deepEqual(vm.waitlistRoomGroupValue,['2nd Room']);
|
||||
vm.onWaitlistRoomRemarkGroupChange(['2nd Room','3rd Room']);
|
||||
assert.deepEqual(vm.waitlistRoomGroupValue,['3rd Room']);
|
||||
vm.onWaitlistRoomRemarkGroupChange([]);assert.deepEqual(vm.waitlistRoomGroupValue,[]);
|
||||
});
|
||||
test('Waitlist remark includes Extend first and preserves date-specific manual text',()=>{
|
||||
const {vm}=setup();vm.showWaitlistPop();vm.onWaitlistRoomRemarkGroupChange(['2nd Room']);
|
||||
vm.changeWaitlistOtherRemark(['Extend','Remark']);vm.unqualifiedDateRemarks[0]='Late arrival';
|
||||
vm.getWaitlistRemark(1,0);
|
||||
assert.equal(vm.bookingData.dateList[0].remark,'Extend; 2nd Room; Late arrival');
|
||||
vm.changeWaitlistOtherRemark([]);vm.onWaitlistRoomRemarkGroupChange([]);vm.unqualifiedDateRemarks[0]='';
|
||||
vm.getWaitlistRemark(1,0);assert.equal(vm.bookingData.dateList[0].remark,'');
|
||||
});
|
||||
test('Waitlist date matching survives reconstructed objects',()=>{
|
||||
const {vm}=setup();vm.showWaitlistPop();vm.changeWaitlistOtherRemark(['Extend','Remark']);
|
||||
vm.unqualifiedDateRemarks[0]='Date note';
|
||||
vm.bookingData.dateList[0]=structuredClone(vm.bookingData.dateList[0]);
|
||||
vm.getWaitlistRemark(1,0);assert.equal(vm.bookingData.dateList[0].remark,'Extend; Date note');
|
||||
});
|
||||
test('different existing and manual remarks are both preserved',()=>{
|
||||
const {vm}=setup();vm.showWaitlistPop();vm.changeWaitlistOtherRemark(['Remark']);
|
||||
vm.bookingData.dateList[0].remarkText='Existing note';vm.unqualifiedDateRemarks[0]='Additional note';
|
||||
vm.getWaitlistRemark(1,0);
|
||||
assert.equal(vm.bookingData.dateList[0].remark,'Existing note; Additional note');
|
||||
});
|
||||
test('extension approval submits extendDate remark and approver fields',async()=>{
|
||||
const {vm,saved,logs}=setup(1);vm.showWaitlistPop();
|
||||
vm.unqualifiedDateReasons[0]='reason';vm.unqualifiedDateApprovers[0]=['7'];
|
||||
vm.unqualifiedDateRemarks[0]='Extension note';vm.changeWaitlistOtherRemark(['Extend','Remark']);
|
||||
vm.selectApproverData=[{value:'7',label:'Approver'}];
|
||||
await vm.submitApprovalRequest();
|
||||
assert.equal(logs.length,0);assert.equal(saved.length,1);
|
||||
assert.equal(saved[0].extendDate[0].remarkText,'Extension note');
|
||||
assert.equal(saved[0].extendDate[0].approver,'7');assert.equal(saved[0].isApproval,1);
|
||||
assert.equal(saved[0].extendDate[0].remark,'Extend; Extension note');
|
||||
assert.equal(saved[0].extendData,undefined);
|
||||
});
|
||||
test('approver response retains Daylight records envelope',async()=>{
|
||||
const {vm}=setup();await vm.getApprover();
|
||||
assert.equal(vm.selectApproverData[0].value,'7');assert.equal(vm.selectApproverData[0].label,'Approver');
|
||||
assert.equal(vm.routingResaon,'Route');
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
async function load(relative) {
|
||||
const source = await readFile(new URL(relative, import.meta.url), 'utf8');
|
||||
return import('data:text/javascript;base64,' + Buffer.from(source).toString('base64'));
|
||||
}
|
||||
const ui = await load('../common/roomCalendarUi.js');
|
||||
const normalizer = await load('../common/normalizeApiKeys.js');
|
||||
const api = await load('../api/roomCalendarApi.js');
|
||||
const configApi = await load('../api/configApi.js');
|
||||
const weekdayLabels = ['SUN','MON','TUE','WED','THU','FRI','SAT'];
|
||||
|
||||
test('API routes preserve auth and request-task options through Daylight transport', async () => {
|
||||
const calls = [];
|
||||
globalThis.uni = { $u: { http: {
|
||||
post: (...args) => { calls.push(['POST',...args]); return Promise.resolve('ok'); },
|
||||
get: (...args) => { calls.push(['GET',...args]); return Promise.resolve('ok'); }
|
||||
}}};
|
||||
try {
|
||||
const params = { Property:'MO', RoomType:'SUITE', PlayerId:'123' };
|
||||
const options = { custom:{auth:true}, getTask:()=>{} };
|
||||
await api.requestRoomCalendar(params,options);
|
||||
await api.getPlayerRoomQualification(params,options);
|
||||
await api.getPropertiesRoomTypes({},options);
|
||||
await configApi.getCalendarSetting({},options);
|
||||
await configApi.getCfgSettingByKey({keyName:'MACAU_STRING'},options);
|
||||
assert.equal(calls[0][1],'/app/room_calendar/request_room_calendar');
|
||||
assert.equal(calls[1][1],calls[0][1]);
|
||||
assert.equal(calls[0][2],params);
|
||||
assert.equal(calls[0][3],options);
|
||||
assert.equal(calls[2][1],'/app/room_calendar/get_properties_room_types');
|
||||
assert.equal(calls[2][2].custom,options.custom);
|
||||
assert.equal(calls[2][2].getTask,options.getTask);
|
||||
assert.equal(calls[3][1],'/app/setting/get_calendar_setting');
|
||||
assert.equal(calls[3][3],options);
|
||||
assert.equal(calls[4][1],'/web/setting/get_public_setting_by_key');
|
||||
} finally { delete globalThis.uni; }
|
||||
});
|
||||
|
||||
test('PascalCase normalization preserves nested values and input objects', () => {
|
||||
const original = { Records:[{RoomType:'SUITE',RoomCount:0,Nested:{Enabled:false}}],Data:null };
|
||||
const snapshot = structuredClone(original);
|
||||
assert.deepEqual(normalizer.normalizeApiKeys(original),
|
||||
{records:[{roomType:'SUITE',roomCount:0,nested:{enabled:false}}],data:null});
|
||||
assert.deepEqual(original,snapshot);
|
||||
assert.deepEqual(normalizer.getApiRecords(original),original.Records);
|
||||
assert.deepEqual(normalizer.getApiRecords({records:[1]}),[1]);
|
||||
assert.deepEqual(normalizer.getApiRecords({Records:{}}),[]);
|
||||
assert.equal(normalizer.normalizeApiKeys(null),null);
|
||||
});
|
||||
|
||||
test('cross-year range has unique month-owned weeks and no visible out-of-range dates', () => {
|
||||
const months = ui.buildCalendarDateRange({
|
||||
today:'2026-12-31',startDate:'2026-12-30',endDate:'2027-01-02',weekdayLabels
|
||||
});
|
||||
assert.deepEqual(months.map(m=>m.monthKey),['2026-12','2027-01']);
|
||||
const weeks = ui.buildContinuousCalendarWeeks(months);
|
||||
assert.equal(new Set(weeks.map(w=>w.weekId)).size,weeks.length);
|
||||
assert.deepEqual(weeks.flatMap(w=>w.days).filter(d=>!d.isOutsideMonth).map(d=>d.date),
|
||||
['2026-12-30','2026-12-31','2027-01-01','2027-01-02']);
|
||||
assert.equal(ui.getTodayWeekId(months),'week-2026-12-2026-12-27');
|
||||
const firstDate = months[0].weeks[0].days[0].date;
|
||||
weeks[0].days[0].date = 'changed';
|
||||
assert.equal(months[0].weeks[0].days[0].date,firstDate);
|
||||
});
|
||||
|
||||
test('leap day is retained and adjacent month cells remain blank', () => {
|
||||
const months = ui.buildCalendarDateRange({
|
||||
today:'2028-02-29',startDate:'2028-02-28',endDate:'2028-03-01',weekdayLabels
|
||||
});
|
||||
const days = ui.buildContinuousCalendarWeeks(months).flatMap(w=>w.days);
|
||||
assert.deepEqual(days.filter(d=>!d.isOutsideMonth).map(d=>d.date),
|
||||
['2028-02-28','2028-02-29','2028-03-01']);
|
||||
assert.equal(days.filter(d=>d.isToday).length,1);
|
||||
assert.ok(days.filter(d=>d.isOutsideMonth).every(d=>d.dayNo===''));
|
||||
});
|
||||
|
||||
test('both response casings map dates counts qualification and leader consistently', () => {
|
||||
const upper = {Date:'2026-09-10T00:00:00',RoomType:'SUITE',RoomCriteria:1200,
|
||||
RoomCount:'2',PropertyLeader:'Leader',BookingDateQualificationFlag:'1'};
|
||||
const lower = normalizer.normalizeApiKeys(upper);
|
||||
const expected = {'2026-09-10_SUITE':{criteria:'1,200',count:2,leader:'Leader',
|
||||
qualified:true,statusText:'Qualified'}};
|
||||
assert.deepEqual(ui.mapRoomCalendarQualificationRecords([upper]),expected);
|
||||
assert.deepEqual(ui.mapRoomCalendarQualificationRecords([lower]),expected);
|
||||
assert.deepEqual(ui.mapRoomCalendarQualificationRecords([{Date:'',RoomType:'X'}]),{});
|
||||
});
|
||||
|
||||
test('zero availability and denied qualification are preserved', () => {
|
||||
const mapped = ui.mapRoomCalendarQualificationRecords([{
|
||||
date:'2026-09-10',roomType:'X',roomCount:0,roomCriteria:null,
|
||||
bookingDateQualificationFlag:0,leader:'Fallback'
|
||||
}])['2026-09-10_X'];
|
||||
assert.equal(mapped.criteria,'-');
|
||||
assert.equal(mapped.leader,'Fallback');
|
||||
assert.equal(ui.getAvailabilityStatusClass(mapped),'zero');
|
||||
assert.equal(ui.getCriteriaStatusClass(mapped),'unqualified');
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { createRequire } from 'node:module';
|
||||
const require = createRequire(import.meta.url);
|
||||
const dayjs = require('../uni_modules/uview-ui/libs/util/dayjs.js');
|
||||
const utilitySource = await readFile(new URL('../common/roomCalendarUi.js',import.meta.url),'utf8');
|
||||
const ui = await import('data:text/javascript;base64,'+Buffer.from(utilitySource).toString('base64'));
|
||||
const pageSource = await readFile(new URL('../pages/roomCalendar/roomCalendar.vue',import.meta.url),'utf8');
|
||||
const appSource = await readFile(new URL('../App.vue',import.meta.url),'utf8');
|
||||
const tabMethod = appSource.slice(appSource.indexOf('showTabbarByPermission(userData){'),
|
||||
appSource.indexOf('// 返回',appSource.indexOf('showTabbarByPermission(userData){'))).trim().replace(/,$/,'');
|
||||
const applyTabPermissions = new Function('uni',`return ({${tabMethod}}).showTabbarByPermission;`);
|
||||
const script = pageSource.match(/<script>([\s\S]*?)<\/script>/)[1]
|
||||
.replace(/import[\s\S]*?from\s+['"][^'"]+['"];?/g,'').replace('export default','return');
|
||||
function setup({permission=true,metadata,availability}={}) {
|
||||
const calls = [];
|
||||
const toasts = [];
|
||||
const user = {canViewCalendar:permission};
|
||||
globalThis.uni = {
|
||||
getLocale:()=> 'en', getStorageSync:()=>user,
|
||||
showLoading(){},hideLoading(){},$u:{toast:m=>toasts.push(m)}
|
||||
};
|
||||
const page = new Function('dayjs','buildCalendarDateRange','getTodayWeekId',
|
||||
'mapRoomCalendarQualificationRecords','getPropertiesRoomTypes','requestRoomCalendar',
|
||||
'requestCalendarSetting',script)(dayjs,ui.buildCalendarDateRange,ui.getTodayWeekId,
|
||||
ui.mapRoomCalendarQualificationRecords,
|
||||
async()=>{calls.push('metadata');return metadata ?? {success:true,propertyList:[
|
||||
{code:'Macau',roomTypes:[{code:'SUITE',label:'SUITE'}],defaultRoomTypes:['SUITE']}
|
||||
]};},
|
||||
async(params,config)=>{calls.push(params);return availability ? availability(params,config) : {success:true,data:[]};},
|
||||
async()=>{calls.push('settings');return {success:true,data:[]};});
|
||||
const vm = {$t:k=>k,$i18n:{},$nextTick:fn=>fn(),showTabbarByPermission(){}};
|
||||
Object.assign(vm,page.data.call(vm));
|
||||
vm.userData = user;
|
||||
for(const [key,fn] of Object.entries(page.methods)) vm[key]=fn.bind(vm);
|
||||
for(const [key,fn] of Object.entries(page.computed)) Object.defineProperty(vm,key,{get:fn.bind(vm)});
|
||||
return {vm,page,calls,toasts};
|
||||
}
|
||||
test('direct navigation and refresh without permission issue no queries',async()=>{
|
||||
const {vm,page,calls}=setup({permission:false});
|
||||
page.onShow.call(vm);
|
||||
vm.refreshCalendar();
|
||||
await vm.loadPropertiesRoomTypes();
|
||||
assert.equal(vm.calendarNoPermission,true);
|
||||
assert.equal(vm.calendarReady,false);
|
||||
assert.deepEqual(calls,[]);
|
||||
});
|
||||
|
||||
test('real tab permission method hides Calendar after an allowed user switches to a denied user',()=>{
|
||||
const visible = new Map();
|
||||
const apply = applyTabPermissions({setTabBarItem:({index,visible:value})=>visible.set(index,value)});
|
||||
apply({canViewCalendar:true});
|
||||
assert.equal(visible.get(3),true);
|
||||
for (const user of [{canViewCalendar:false},{},{canViewCalendar:'true'},null,undefined]) {
|
||||
apply({canViewCalendar:true});
|
||||
apply(user);
|
||||
assert.equal(visible.get(3),false);
|
||||
}
|
||||
});
|
||||
|
||||
test('page onShow applies real tab visibility and blocks denied users before any query',()=>{
|
||||
const {vm,page,calls}=setup({permission:false});
|
||||
const visible = new Map();
|
||||
vm.showTabbarByPermission = applyTabPermissions({setTabBarItem:({index,visible:value})=>visible.set(index,value)});
|
||||
page.onShow.call(vm);
|
||||
assert.equal(visible.get(3),false);
|
||||
assert.equal(vm.calendarNoPermission,true);
|
||||
assert.deepEqual(calls,[]);
|
||||
});
|
||||
test('metadata denial handles Daylight code and stops availability request',async()=>{
|
||||
const {vm,calls}=setup({metadata:{success:false,code:'NO_PERMISSION'}});
|
||||
await vm.loadPropertiesRoomTypes();
|
||||
assert.equal(vm.calendarNoPermission,true);
|
||||
assert.equal(vm.calendarReady,false);
|
||||
assert.deepEqual(calls,['settings','metadata']);
|
||||
});
|
||||
test('empty property list is reported without accessing first element',async()=>{
|
||||
const {vm,calls,toasts}=setup({metadata:{success:true,propertyList:[]}});
|
||||
await vm.loadPropertiesRoomTypes();
|
||||
assert.equal(vm.calendarReady,false);
|
||||
assert.deepEqual(calls,['settings','metadata']);
|
||||
assert.equal(toasts[0],'No calendar properties configured');
|
||||
});
|
||||
test('initial availability denial does not re-enable the grid',async()=>{
|
||||
const {vm}=setup({availability:async()=>({success:false,errorCode:'NO_PERMISSION'})});
|
||||
await vm.loadPropertiesRoomTypes();
|
||||
assert.equal(vm.calendarNoPermission,true);
|
||||
assert.equal(vm.calendarReady,false);
|
||||
});
|
||||
test('older availability response cannot overwrite a newer selection',async()=>{
|
||||
const pending=[];
|
||||
const {vm}=setup({availability:()=>new Promise(resolve=>pending.push(resolve))});
|
||||
vm.propertyList=[{code:'Macau',roomTypes:[{code:'SUITE'}]}];
|
||||
vm.selectedPropertyCode='Macau';vm.selectedRoomTypeCodes=['SUITE'];
|
||||
vm.buildCalendar();
|
||||
const first=vm.loadAvailabilityData();
|
||||
const second=vm.loadAvailabilityData();
|
||||
const date=vm.getCalendarDateRange().startDate;
|
||||
pending[1]({success:true,data:[{date,roomType:'SUITE',roomCount:2}]});
|
||||
await second;
|
||||
pending[0]({success:true,data:[{date,roomType:'SUITE',roomCount:9}]});
|
||||
await first;
|
||||
assert.equal(vm.availabilityMap[date+'_SUITE'].count,2);
|
||||
});
|
||||
test('OTHER request sends real hotel codes and deduplicated room types',()=>{
|
||||
const {vm}=setup();
|
||||
vm.propertyList=[{code:'OTHER',otherProperties:[
|
||||
{code:'A',roomTypes:[{code:'SUITE'}]},{code:'B',roomTypes:[{code:'SUITE'}]}
|
||||
]}];
|
||||
vm.selectedPropertyCode='OTHER';vm.selectedOtherPropertyCodes=['A','B'];
|
||||
assert.deepEqual(vm.buildAvailabilityParams({startDate:'2026-09-01',endDate:'2026-09-02'}),
|
||||
{Property:'A,B',RoomType:'SUITE',StartDate:'2026-09-01',EndDate:'2026-09-02'});
|
||||
});
|
||||
test('room selection cap retains existing selection',()=>{
|
||||
const {vm}=setup();
|
||||
vm.maxSelectedRoomTypes=1;vm.selectedRoomTypeCodes=['A'];
|
||||
vm.toggleRoomType('B');
|
||||
assert.deepEqual(vm.selectedRoomTypeCodes,['A']);
|
||||
});
|
||||
test('login remains launch page and calendar/settings labels have translations',async()=>{
|
||||
const raw=await readFile(new URL('../pages.json',import.meta.url),'utf8');
|
||||
const routes=JSON.parse(raw.replace(/^\s*\/\/.*$/gm,''));
|
||||
assert.equal(routes.pages[0].path,'pages/login/login');
|
||||
assert.deepEqual(routes.tabBar.list.map(t=>t.pagePath),[
|
||||
'pages/dashboard/dashboard','salessupport/todoList/todoList','pages/findPlayer/findPlayer',
|
||||
'pages/roomCalendar/roomCalendar','pages/setting/setting']);
|
||||
assert.equal(routes.tabBar.list[3].visible,false);
|
||||
for(const locale of ['en','zh-Hant']){
|
||||
const messages=JSON.parse(await readFile(new URL('../locale/'+locale+'.json',import.meta.url),'utf8'));
|
||||
assert.ok(messages['roomCalendar.title']);
|
||||
assert.ok(messages['roomCalendar.maxSelectedRoomTypes']);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {readFile} from 'node:fs/promises';
|
||||
const source = await readFile(new URL('../pages/dashboard/dashboard.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');
|
||||
function setup(data) {
|
||||
let requests=0;
|
||||
const page = new Function('mgmHeader','uni','getTodoDashboard',script)({},
|
||||
{showLoading(){},hideLoading(){}},async()=>{requests++;return typeof data === 'function' ? data() : {success:true,data};});
|
||||
const vm={$t:k=>k};
|
||||
Object.assign(vm,page.data.call(vm),{userData:{userId:'80001'}});
|
||||
for(const [key,fn] of Object.entries(page.methods)) vm[key]=fn.bind(vm);
|
||||
return {vm,requests:()=>requests};
|
||||
}
|
||||
test('Dashboard request accepts both response casings and sets IM totals and data date',async()=>{
|
||||
for(const pascal of [false,true]) {
|
||||
const records=[{currentValue:0,itemName:'MF Turnover',userSegment:'IM',type:'Achievement',targetValue:100},
|
||||
{itemName:'2026-09-09',type:'DataAsOf'}];
|
||||
const data=pascal?{Records:records.map(r=>Object.fromEntries(Object.entries(r).map(([k,v])=>[k[0].toUpperCase()+k.slice(1),v])))}:{records};
|
||||
const {vm,requests}=setup(data);
|
||||
await vm.getTodoDashboardByuserId();
|
||||
assert.equal(requests(),1);
|
||||
assert.equal(vm.showDashboardView,true);
|
||||
assert.equal(vm.userSegment,'IM');
|
||||
assert.equal(vm.MFValue,0);
|
||||
assert.equal(vm.dataAsOf,'2026-09-09');
|
||||
}
|
||||
});
|
||||
test('Dashboard shows only populated sections and preserves IM turnover-only Achievement',async()=>{
|
||||
for(const [records,achievement,kpi] of [
|
||||
[[{type:'KPI',itemName:'Rate'}],false,true],
|
||||
[[{type:'Achievement',itemName:'Target'}],true,false],
|
||||
[[{type:'Achievement',itemName:'MF Turnover',userSegment:'IM',currentValue:0}],true,false]
|
||||
]) {
|
||||
const {vm}=setup({records}); await vm.getTodoDashboardByuserId();
|
||||
assert.equal(vm.showAchievement,achievement); assert.equal(vm.showKPI,kpi);
|
||||
}
|
||||
});
|
||||
test('Dashboard empty refresh clears old data and hides both sections',async()=>{
|
||||
const data={records:[{type:'Achievement',itemName:'MF Turnover',userSegment:'IM',currentValue:99}]};
|
||||
const {vm}=setup(data); await vm.getTodoDashboardByuserId();
|
||||
data.records=[]; await vm.getTodoDashboardByuserId();
|
||||
assert.deepEqual(vm.dashboardData,[]); assert.equal(vm.MFValue,'0');
|
||||
assert.equal(vm.showDashboardView,false); assert.equal(vm.showAchievement,false); assert.equal(vm.showKPI,false);
|
||||
});
|
||||
test('Dashboard failed refresh hides previously displayed data',async()=>{
|
||||
let fail=false;
|
||||
const {vm}=setup(()=>{if(fail)throw Error('network');return {success:true,data:{records:[{type:'KPI',itemName:'Rate'}]}};});
|
||||
await vm.getTodoDashboardByuserId(); fail=true; await vm.getTodoDashboardByuserId();
|
||||
assert.equal(vm.showDashboardView,false); assert.equal(vm.showKPI,false); assert.equal(vm.showAchievement,false);
|
||||
});
|
||||
test('Dashboard normalization preserves camelCase zero over PascalCase fallback',()=>{
|
||||
const {vm}=setup({});
|
||||
const record={currentValue:0,CurrentValue:99,targetValue:0,TargetValue:100};
|
||||
const normalized=vm.normalizeDashboardRecord(record);
|
||||
assert.equal(normalized.currentValue,0);
|
||||
assert.equal(normalized.targetValue,0);
|
||||
assert.equal(record.CurrentValue,99);
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {readFile} from 'node:fs/promises';
|
||||
import {createRequire} from 'node:module';
|
||||
const require = createRequire(import.meta.url);
|
||||
const dayjs = require('../uni_modules/uview-ui/libs/util/dayjs.js');
|
||||
const source = await readFile(new URL('../booking/playerRoomCalendar/playerRoomCalendar.vue', import.meta.url), 'utf8');
|
||||
const script = source.match(/<script>([\s\S]*?)<\/script>/)[1]
|
||||
.replace(/import[\s\S]*?from\s+['"][^'"]+['"];?/g, '').replace('export default', 'return');
|
||||
const utility = await readFile(new URL('../common/roomCalendarUi.js', import.meta.url), 'utf8');
|
||||
const ui = await import('data:text/javascript;base64,' + Buffer.from(utility).toString('base64'));
|
||||
function setup({permission=true, player={playerId:'80001'}, metadata, response}={}) {
|
||||
const calls = [], toasts = [];
|
||||
const user = {canViewCalendar:permission};
|
||||
const uni = {getStorageSync:key=>key==='userData'?user:player,
|
||||
showLoading(){},hideLoading(){},$u:{toast:m=>toasts.push(m)}};
|
||||
const page = new Function('uni','dayjs','buildCalendarDateRange','getTodayWeekId',
|
||||
'mapRoomCalendarQualificationRecords','getPropertiesRoomTypes','getPlayerRoomQualification',
|
||||
'requestCalendarSetting',script)(uni,dayjs,ui.buildCalendarDateRange,ui.getTodayWeekId,
|
||||
ui.mapRoomCalendarQualificationRecords,
|
||||
async()=>{calls.push('metadata'); return metadata ?? {propertyList:[
|
||||
{code:'Macau',name:'Macau',roomTypes:[{code:'SUITE',label:'SUITE'}],defaultRoomTypes:['SUITE']}
|
||||
]};},
|
||||
async p=>{calls.push(p);return response ?? {success:true,data:[]};},
|
||||
async()=>{calls.push('settings');return {data:[]};});
|
||||
const vm = {$t:k=>k,$nextTick:fn=>fn()};
|
||||
Object.assign(vm,page.data.call(vm),{playerData:player,userData:user});
|
||||
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)});
|
||||
return {vm,page,calls,toasts};
|
||||
}
|
||||
test('denied direct entry and refresh make no requests',async()=>{
|
||||
for (const permission of [false,undefined,'true']) {
|
||||
const {vm,calls}=setup({permission:permission ?? null});
|
||||
await vm.loadPropertiesRoomTypes(); vm.refreshCalendar(); await vm.loadQualificationData();
|
||||
assert.deepEqual(calls,[]); assert.equal(vm.calendarNoPermission,true);
|
||||
}
|
||||
});
|
||||
test('missing player cannot accidentally request the general Calendar',async()=>{
|
||||
const {vm,calls}=setup({player:null});
|
||||
await vm.loadPropertiesRoomTypes(); await vm.loadQualificationData();
|
||||
assert.deepEqual(calls,[]); assert.equal(vm.calendarReady,false);
|
||||
});
|
||||
test('player query carries current player, property, room type and date range',async()=>{
|
||||
const {vm,calls}=setup(); await vm.loadPropertiesRoomTypes();
|
||||
const query=calls.find(c=>typeof c==='object');
|
||||
assert.equal(query.PlayerId,'80001'); assert.equal(query.Property,'Macau');
|
||||
assert.equal(query.RoomType,'SUITE');
|
||||
assert.match(query.StartDate,/^\d{4}-\d{2}-\d{2}$/);
|
||||
assert.ok(query.EndDate>=query.StartDate); assert.equal(vm.calendarReady,true);
|
||||
});
|
||||
test('metadata denial and empty configuration do not call player query',async()=>{
|
||||
for (const metadata of [{success:false,code:'NO_PERMISSION'},{propertyList:[]}]) {
|
||||
const {vm,calls}=setup({metadata}); await vm.loadPropertiesRoomTypes();
|
||||
assert.deepEqual(calls,['settings','metadata']); assert.equal(vm.calendarReady,false);
|
||||
}
|
||||
});
|
||||
test('both denial response formats remain denied after initial load completes',async()=>{
|
||||
for (const key of ['code','errorCode']) {
|
||||
const {vm}=setup({response:{success:false,[key]:'NO_PERMISSION'}});
|
||||
await vm.loadPropertiesRoomTypes();
|
||||
assert.equal(vm.calendarReady,false); assert.equal(vm.calendarNoPermission,true);
|
||||
assert.deepEqual(vm.qualificationMap,{});
|
||||
}
|
||||
});
|
||||
test('booking entry rechecks permission before navigation',async()=>{
|
||||
const booking=await readFile(new URL('../booking/bookingRecord/bookingRecord.vue',import.meta.url),'utf8');
|
||||
const method=booking.match(/navToPlayerRoomCalendar\(\)\{([\s\S]*?)\n\s*\},/)[1];
|
||||
const visits=[];
|
||||
for (const permission of [false,true]) {
|
||||
new Function('uni',method).call({navTo:(...args)=>visits.push(args)},
|
||||
{getStorageSync:()=>({canViewCalendar:permission})});
|
||||
}
|
||||
assert.deepEqual(visits,[['/booking/playerRoomCalendar/playerRoomCalendar',0,false]]);
|
||||
assert.match(booking, /<image\s+v-if="userData && userData\.canViewCalendar === true"\s+@click="navToPlayerRoomCalendar"[^>]*><\/image>\s*<image\s+v-else\s+@click="getDataByPage"[^>]*><\/image>/);
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import vm from 'node:vm';
|
||||
|
||||
const root = new URL('../', import.meta.url);
|
||||
const app = fs.readFileSync(new URL('App.vue', root), 'utf8');
|
||||
const method = app.slice(app.indexOf('async writeLog('), app.indexOf('async logout(){'));
|
||||
|
||||
test('actual APP log method routes by token and preserves debugMode gate', async () => {
|
||||
for (const token of ['', null, undefined, 'valid-token']) {
|
||||
const calls = [];
|
||||
const scope = {
|
||||
uni: {getStorageSync: key => key === 'token' ? token : {debugMode: true}},
|
||||
writeDebugLog: async value => calls.push(['private', value]),
|
||||
writePublicDebugLog: async value => calls.push(['public', value]), console,
|
||||
};
|
||||
const {writeLog} = vm.runInNewContext(`({${method}})`, scope);
|
||||
await writeLog('80001', 'LOGIN', 'txn', 'description', null, null, 1, false);
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(calls[0][0], token ? 'private' : 'public');
|
||||
assert.equal(calls[0][1].eventCode, 'LOGIN');
|
||||
scope.uni.getStorageSync = key => key === 'token' ? token : {debugMode: false};
|
||||
await writeLog('80001', 'LOGIN', 'txn', 'description', null, null, 1, false);
|
||||
assert.equal(calls.length, 1);
|
||||
}
|
||||
});
|
||||
|
||||
test('API wrappers use public bootstrap routes and preserve refresh protocol', async () => {
|
||||
const calls = [];
|
||||
const scope = {uni: {$u: {http: {post: (...args) => calls.push(args)}}}};
|
||||
function load(file, names) {
|
||||
const source = fs.readFileSync(new URL(file, root), 'utf8').replaceAll('export const ', 'const ');
|
||||
return vm.runInNewContext(`${source}\n;({${names.join(',')}})`, scope);
|
||||
}
|
||||
const common = load('api/commonApi.js', ['getSettings', 'writeDebugLog', 'writePublicDebugLog', 'refreshToken']);
|
||||
const config = load('api/configApi.js', ['getCfgSettingByKey']);
|
||||
const params = {keyName: 'MACAU_STRING'};
|
||||
const options = {custom: {test: true}};
|
||||
common.getSettings(params, options); common.writeDebugLog(params, options);
|
||||
common.writePublicDebugLog(params, options); config.getCfgSettingByKey(params, options);
|
||||
common.refreshToken(params, options);
|
||||
assert.deepEqual(calls.map(call => call[0]), [
|
||||
'/system/sys_setting/get_public_login_settings', '/system/write_log', '/system/write_public_log',
|
||||
'/web/setting/get_public_setting_by_key', '/app/refresh_token?isRenewRefreshToken=false',
|
||||
]);
|
||||
for (const call of calls) { assert.equal(call[1], params); assert.equal(call[2], options); }
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import vm from 'node:vm';
|
||||
const source=fs.readFileSync(new URL('../common/request.js',import.meta.url),'utf8').replace(/^import .*$/mg,'');
|
||||
function harness(refresh) {
|
||||
const storage=new Map([['token','old'],['refreshToken','refresh'],['clientId','client']]);
|
||||
let failure, retried=0, redirects=0;
|
||||
const context={module:{exports:{}},console:{log(){}},dayjs:()=>({unix:()=>123}),refreshToken:refresh,
|
||||
uni:{getStorageSync:k=>storage.get(k),setStorageSync:(k,v)=>storage.set(k,v),removeStorageSync:k=>storage.delete(k),
|
||||
redirectTo:()=>redirects++, $u:{toast(){},http:{request:async()=>{retried++;return 'retried';},
|
||||
interceptors:{request:{use(){}},response:{use:(ok,bad)=>failure=bad}}}}}};
|
||||
vm.runInNewContext(source,context); context.module.exports({});
|
||||
return {storage, failure:e=>failure(e), counts:()=>({retried,redirects})};
|
||||
}
|
||||
const unauthorized=()=>({statusCode:401,config:{url:'/app/me',header:{},custom:{auth:true}}});
|
||||
test('successful refresh retries once with new access credential', async()=>{
|
||||
const h=harness(async()=>({success:true,accessToken:'new'})); const error=unauthorized();
|
||||
assert.equal(await h.failure(error),'retried'); assert.equal(error.config.header.Authorization,'new');
|
||||
assert.equal(h.storage.get('refreshToken'),'refresh');
|
||||
await assert.rejects(h.failure(error)); assert.equal(h.counts().retried,1);
|
||||
});
|
||||
test('failed, malformed or missing refresh stops original request',async()=>{
|
||||
for(const mode of ['failure','malformed','missing']) {
|
||||
let calls=0;
|
||||
const h=harness(async()=>{calls++;if(mode==='failure')throw new Error('denied');return {success:false};});
|
||||
if(mode==='missing')h.storage.delete('refreshToken');
|
||||
await assert.rejects(h.failure(unauthorized()));
|
||||
assert.equal(h.counts().retried,0);assert.equal(h.storage.has('token'),false);
|
||||
assert.equal(calls,mode==='missing'?0:1);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user