This commit is contained in:
Mauve
2026-09-16 10:43:59 +08:00
parent a7f2e0ca01
commit 9f2becfa67
36 changed files with 3997 additions and 264 deletions
+134
View File
@@ -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']);
}
});