319 lines
21 KiB
JavaScript
319 lines
21 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const vm = require('node:vm');
|
|
const { stripTypeScriptTypes } = require('node:module');
|
|
const read = p => fs.readFileSync(require('node:path').join(__dirname, '..', p), 'utf8');
|
|
const clean = s => s.replace(/^import .*;\r?$/gm, '').replace(/^export type /gm, 'type ').replace('export default class', 'class');
|
|
const tool = read('assets/Script/module/Tool/GameTool.ts');
|
|
const utils = read('assets/Script/module/Pay/Utils.ts');
|
|
const code = stripTypeScriptTypes([
|
|
clean(read('assets/Script/module/Task/DailyTaskProgressTracker.ts')),
|
|
clean(read('assets/Script/module/Task/DailyTaskSync.ts')),
|
|
'class Utils {' + utils.slice(utils.indexOf(' //每日任务获取'), utils.indexOf(' //设置连胜次数')) + '}',
|
|
'const GameTool = {' + tool.slice(tool.indexOf(' getDailyQuestsInfo('), tool.indexOf(' recordDailyTaskShare(')) + '};',
|
|
'globalThis.classes = {Tracker: DailyTaskProgressTracker, Sync: DailyTaskSync, Utils, GameTool};',
|
|
].join('\n'));
|
|
|
|
function setup() {
|
|
let now = Date.parse('2026-09-08T04:00:00Z'), timerId = 0;
|
|
const timers = new Map(), requests = [], notifications = [];
|
|
const info = {uid: 'test-user', otherLevel: 0, tasks: null};
|
|
const addTimer = (fn, delay, interval = 0) => { const id = ++timerId; timers.set(id, {fn, at: now + delay, interval}); return id; };
|
|
const context = {wx: {}, console: {log() {}}, Date: {now: () => now},
|
|
setTimeout: (fn, delay) => addTimer(fn, delay), clearTimeout: id => timers.delete(id),
|
|
setInterval: (fn, delay) => addTimer(fn, delay, delay), clearInterval: id => timers.delete(id),
|
|
cc: {director: {emit: e => notifications.push(e)}, fx: {GameConfig: {GM_INFO: info}, StorageMessage: {getStorage: () => info.uid}}},
|
|
};
|
|
vm.runInNewContext(code, context);
|
|
const {Tracker, Sync, Utils, GameTool} = context.classes;
|
|
context.cc.fx.GameTool = GameTool;
|
|
GameTool.shushu_Track = () => {};
|
|
GameTool.getWechatGameVersion = () => '开发版';
|
|
Utils.POST = (endpoint, fields, callback) => requests.push({endpoint, fields, callback, kind: fields.action || 'claim'});
|
|
const data = (extra = {}) => ({version: 2, dayKey: 'day1', weekKey: 'week1', serverNow: now,
|
|
dayEndsAt: now + 86400000, weekEndsAt: now + 7 * 86400000, weeklyStars: 0,
|
|
dailyTasks: [{taskId: 'pass', metric: 'pass_levels', target: 10, value: 0, state: 0}],
|
|
weeklyRewards: [{rewardId: 'weekly_35', target: 35, state: 0, rewards: [{type: 'coin', count: 10}]}], ...extra});
|
|
const respond = (task, index = 0, extras = {}) => {
|
|
const request = requests.splice(index, 1)[0]; assert.ok(request, 'a request is pending');
|
|
request.callback({code: 1, data: {task: structuredClone(task), ...extras}}); return request;
|
|
};
|
|
const advance = ms => {
|
|
const end = now + ms;
|
|
while (true) {
|
|
const next = [...timers].filter(([, t]) => t.at <= end).sort((a,b) => a[1].at - b[1].at)[0];
|
|
if (!next) break;
|
|
const [id,t] = next; now = t.at;
|
|
if (t.interval) t.at += t.interval; else timers.delete(id);
|
|
t.fn();
|
|
}
|
|
now = end;
|
|
};
|
|
const load = task => { GameTool.getDailyQuestsInfo(() => {}); respond(task || data()); };
|
|
const ackSave = () => { const snapshot = JSON.parse(requests[0].fields.task); snapshot.serverNow = now; respond(snapshot); };
|
|
return {Tracker, Sync, Utils, GameTool, info, context, requests, notifications, data, respond, advance, load, ackSave,
|
|
get now() {return now;}, value: () => info.tasks.dailyTasks[0].value};
|
|
}
|
|
|
|
test('first reads coalesce and subsequent opens reuse the same valid in-memory task data', () => {
|
|
const h = setup(), replies = [];
|
|
h.GameTool.getDailyQuestsInfo(r => replies.push(r)); h.GameTool.getDailyQuestsInfo(r => replies.push(r));
|
|
assert.equal(h.requests.length, 1); h.respond(h.data()); assert.equal(replies.length, 2);
|
|
for (let i=0; i<5; i++) h.GameTool.getDailyQuestsInfo(r => assert.equal(r.data.task, h.info.tasks));
|
|
assert.equal(h.requests.length, 0);
|
|
h.advance(20000); assert.equal(h.Sync.now(), h.now, 'reading cached data never recalibrates the clock');
|
|
});
|
|
|
|
test('expiry uses server midnight for either daily or weekly boundary and coalesces refreshes', () => {
|
|
for (const field of ['dayEndsAt', 'weekEndsAt']) {
|
|
const h=setup(), serverNow=h.now+3600000;
|
|
h.load(h.data({serverNow, dayEndsAt: serverNow+86400000, weekEndsAt: serverNow+7*86400000, [field]: serverNow+10000}));
|
|
h.advance(9999); assert.ok(h.Sync.cached()); h.advance(1); assert.equal(h.Sync.cached(), null);
|
|
h.GameTool.getDailyQuestsInfo(()=>{}); h.GameTool.getDailyQuestsInfo(()=>{});
|
|
assert.equal(h.requests.length,1); assert.equal(h.requests[0].kind,'read');
|
|
h.respond(h.data({dayKey: field==='dayEndsAt'?'day2':'day1', weekKey:'week2'})); assert.ok(h.Sync.cached());
|
|
}
|
|
});
|
|
|
|
test('save responses preserve later progress and continue uploading before completing synchronization', () => {
|
|
const h=setup(); h.load(); h.Tracker.record({metric:'pass_levels',amount:3}); h.advance(1000);
|
|
assert.equal(h.requests[0].kind,'save'); assert.equal(JSON.parse(h.requests[0].fields.task).dailyTasks[0].value,3);
|
|
h.Tracker.record({metric:'pass_levels'}); h.ackSave();
|
|
assert.equal(h.value(),4); assert.equal(h.requests.length,1);
|
|
assert.equal(JSON.parse(h.requests[0].fields.task).dailyTasks[0].value,4);
|
|
h.ackSave(); h.advance(1000); assert.equal(h.requests.length,0);
|
|
});
|
|
|
|
test('a win streak reset during a save is preserved even if later wins exceed the submitted value', () => {
|
|
const h=setup(); h.load(h.data({dailyTasks:[{taskId:'streak',metric:'win_streak',value:0,target:10,state:0}]}));
|
|
h.Tracker.record({metric:'win_streak',amount:3}); h.advance(1000);
|
|
h.Tracker.resetWinStreak(); h.Tracker.record({metric:'win_streak',amount:4});
|
|
const server=JSON.parse(h.requests[0].fields.task); server.dailyTasks[0].value=6; h.respond(server);
|
|
assert.equal(h.value(),4); h.ackSave(); assert.equal(h.value(),4);
|
|
});
|
|
|
|
test('daily and weekly claims wait for pending saves and synchronize returned state exactly once', () => {
|
|
for (const scope of ['daily','weekly']) {
|
|
const h=setup(); const data=h.data(); data.dailyTasks[0].target=1; data.weeklyRewards[0].state=1; h.load(data);
|
|
h.Tracker.record({metric:'pass_levels'}); let result;
|
|
h.GameTool.claimDailyQuestReward(scope==='daily'?'pass':'weekly_35',r=>{result=r;},scope);
|
|
assert.deepEqual(h.requests.map(r=>r.kind),['save']); h.ackSave();
|
|
assert.deepEqual(h.requests.map(r=>r.kind),['claim']);
|
|
const updated=structuredClone(h.info.tasks);
|
|
(scope==='daily'?updated.dailyTasks:updated.weeklyRewards)[0].state=2; updated.weeklyStars=35;
|
|
h.respond(updated,0,{rewards:[{type:'coin',count:10}]});
|
|
assert.equal(result.code,1); assert.equal(h.info.tasks.weeklyStars,35);
|
|
h.GameTool.claimDailyQuestReward(scope==='daily'?'pass':'weekly_35',()=>{},scope);
|
|
assert.equal(h.requests.length,0, 'already claimed state cannot issue another grant request');
|
|
}
|
|
});
|
|
|
|
test('failed saves block claims, preserve unsaved progress and release the queue for retry', () => {
|
|
const h=setup(); const data=h.data(); data.dailyTasks[0].target=1; h.load(data); h.Tracker.record({metric:'pass_levels'});
|
|
const replies=[]; h.GameTool.claimDailyQuestReward('pass',r=>replies.push(r));
|
|
h.requests.shift().callback({code:0,msg:'offline'});
|
|
assert.ok(h.requests.every(r=>r.kind!=='claim')); assert.equal(h.value(),1);
|
|
assert.equal(replies[0].code,0);
|
|
assert.equal(h.requests.length,0);
|
|
h.GameTool.claimDailyQuestReward('pass',()=>{}); assert.equal(h.requests[0].kind,'save');
|
|
h.ackSave(); assert.equal(h.requests[0].kind,'claim');
|
|
});
|
|
|
|
test('new-period events are buffered while reading and old unsaved progress is never uploaded into the new day', () => {
|
|
const h=setup(); h.load(h.data({dayEndsAt:h.now+500}));
|
|
h.Tracker.record({metric:'pass_levels',amount:3}); h.advance(600);
|
|
h.Tracker.record({metric:'pass_levels',amount:2});
|
|
assert.deepEqual(h.requests.map(r=>r.kind),['read']);
|
|
h.respond(h.data({dayKey:'day2'})); assert.equal(h.value(),2);
|
|
h.advance(1000); assert.equal(h.requests[0].kind,'save');
|
|
assert.equal(JSON.parse(h.requests[0].fields.task).dayKey,'day2');
|
|
assert.equal(JSON.parse(h.requests[0].fields.task).dailyTasks[0].value,2);
|
|
});
|
|
|
|
test('online recovery before initial loading is retained and uploaded through the synchronization queue', () => {
|
|
const h=setup(); h.Tracker.startOnline(); h.advance(65000);
|
|
h.load(h.data({dailyTasks:[{taskId:'online',metric:'online_seconds',value:10,target:300,state:0}]}));
|
|
assert.equal(h.value(),70); h.advance(1000); h.ackSave(); h.advance(24000); assert.equal(h.value(),100);
|
|
});
|
|
|
|
test('online settlement spanning midnight counts only new-day seconds in the replacement tasks', () => {
|
|
const h=setup(); h.load(h.data({dayEndsAt:h.now+10000,dailyTasks:[{taskId:'online',metric:'online_seconds',value:0,target:300,state:0}]}));
|
|
h.Tracker.startOnline(); h.advance(30000); assert.equal(h.requests[0].kind,'read');
|
|
h.respond(h.data({dayKey:'day2',dailyTasks:[{taskId:'online',metric:'online_seconds',value:0,target:300,state:0}]}));
|
|
assert.equal(h.value(),20);
|
|
});
|
|
|
|
test('debug pause drains uploads, ignores new events, refreshes external resets and resumes without paused time', () => {
|
|
const h=setup(); h.load(); h.Tracker.record({metric:'pass_levels',amount:3}); h.advance(1000);
|
|
let paused; h.GameTool.debugDailyTasks('pause',r=>paused=r);
|
|
assert.equal(paused,undefined); h.Tracker.record({metric:'pass_levels',amount:5}); assert.equal(h.value(),3);
|
|
h.ackSave(); assert.equal(paused.code,1);
|
|
h.GameTool.debugDailyTasks('refresh',()=>{}); assert.equal(h.requests[0].kind,'read'); h.respond(h.data()); assert.equal(h.value(),0);
|
|
h.advance(30000); assert.equal(h.requests.length,0);
|
|
h.GameTool.debugDailyTasks('resume',()=>{}); h.respond(h.data()); h.Tracker.record({metric:'pass_levels'}); assert.equal(h.value(),1);
|
|
});
|
|
|
|
test('debug controls are unavailable outside development and long foreground absence refreshes once', () => {
|
|
const h=setup(); h.load(); h.GameTool.getWechatGameVersion=()=> '正式版';
|
|
let result; h.GameTool.debugDailyTasks('pause',r=>result=r); assert.equal(result.code,0);
|
|
h.GameTool.onDailyTasksBackground(); h.advance(1000); h.GameTool.onDailyTasksForeground(); assert.equal(h.requests.length,0);
|
|
h.GameTool.onDailyTasksBackground(); h.advance(300000); h.GameTool.onDailyTasksForeground(); h.GameTool.onDailyTasksForeground();
|
|
assert.equal(h.requests.length,1);
|
|
});
|
|
|
|
test('network restoration retries failed reads and stale-account callbacks cannot populate the new account cache', () => {
|
|
const h=setup(); h.load(); h.GameTool.onDailyTaskNetworkChange(false); h.GameTool.onDailyTaskNetworkChange(true);
|
|
assert.equal(h.requests.length,1); h.requests.shift().callback({code:0,msg:'offline'});
|
|
h.GameTool.onDailyTaskNetworkChange(true); assert.equal(h.requests.length,1);
|
|
h.info.uid='another-user'; h.respond(h.data()); assert.equal(h.Sync.cached(),null);
|
|
h.GameTool.getDailyQuestsInfo(()=>{}); h.respond(h.data()); assert.ok(h.Sync.cached());
|
|
});
|
|
|
|
test('invalid or expired server task snapshots are not exposed as valid cache entries', () => {
|
|
const h=setup(); let result; h.GameTool.getDailyQuestsInfo(r=>result=r);
|
|
h.respond(h.data({dayEndsAt:h.now-1})); assert.equal(result.code,0); assert.equal(h.Sync.cached(),null);
|
|
h.GameTool.getDailyQuestsInfo(r=>result=r); h.requests.shift().callback({code:1,data:{task:{version:2}}});
|
|
assert.equal(result.code,0); assert.equal(h.Sync.cached(),null);
|
|
for (const invalid of [null, h.data({version: 1}), h.data({version: 3}), h.data({version: '2'}), h.data({dailyTasks: {}})]) {
|
|
const valid = h.data(); h.Tracker.setTaskData(valid);
|
|
h.Tracker.setTaskData(invalid);
|
|
assert.equal(h.Tracker.getTaskData(), valid, 'rejected snapshots must not replace local progress');
|
|
h.info.tasks = invalid;
|
|
assert.equal(h.Tracker.getTaskData(), null, 'direct cache reads use the same compatibility rules');
|
|
h.GameTool.getDailyQuestsInfo(r=>result=r); h.respond(invalid);
|
|
assert.equal(result.code,0); assert.equal(h.Sync.cached(),null);
|
|
}
|
|
h.GameTool.getDailyQuestsInfo(r=>result=r); h.respond(h.data({weeklyRewards: null}));
|
|
assert.equal(result.code,0); assert.equal(h.Sync.cached(),null, 'responses still require weekly rewards');
|
|
});
|
|
|
|
test('malformed claim responses cannot be treated as grants or leave the request queue occupied', () => {
|
|
const h=setup(),data=h.data();data.dailyTasks[0].state=1;data.dailyTasks[0].value=10;h.load(data);
|
|
let result;h.GameTool.claimDailyQuestReward('pass',r=>result=r);h.requests.shift().callback({code:1,data:{}});
|
|
assert.equal(result.code,0);assert.equal(h.info.tasks.dailyTasks[0].state,1);assert.equal(h.Sync.busy,false);
|
|
});
|
|
|
|
function method(source, name) {
|
|
const start=source.search(new RegExp(`^ (?:private |public |static )*${name}\\(`,'m'));
|
|
assert.notEqual(start,-1); const tail=source.slice(start), end=tail.search(/^ }\r?$/m);
|
|
return tail.slice(0,end+5);
|
|
}
|
|
|
|
function installUI(h) {
|
|
const homeSource=read('assets/Script/JiaZai.ts'), panelSource=read('assets/action_bundle/script/DailyQuests.ts');
|
|
const uiCode=stripTypeScriptTypes(`class JiaZai {${['openDailyQuests','checkTasks','refreshTaskRedDot','refreshTaskViews'].map(n=>method(homeSource,n)).join('\n')}}
|
|
class Panel {${['closeDailyQuests','refreshFromCache','initCountdowns','updateCountdowns','hasExpiredDisplayedTasks','getRemainingSeconds','formatClock'].map(n=>method(panelSource,n)).join('\n')}}
|
|
globalThis.UIClasses={JiaZai,Panel};`);
|
|
vm.runInNewContext(uiCode,h.context);
|
|
const {JiaZai,Panel}=h.context.UIClasses, home=new JiaZai(), panel=new Panel(), red={active:false}, day={text:''}, week={text:''};
|
|
const top={getChildByName:()=>({getChildByName:()=>red})};
|
|
const root={getChildByName:()=>({getChildByName:()=>top}),getComponent:()=>home,addChild:()=>{}};
|
|
const bg={getChildByName:n=>n==='dayCountDown'?day:week};
|
|
panel.node={active:true,get activeInHierarchy(){return this.active;},getChildByName:()=>bg,getComponent:()=>panel};
|
|
panel.dailyTaskStateAnimations=[];
|
|
panel.setNodeText=(node,value)=>{node.text=value;};
|
|
let timer; panel.unschedule=()=>h.context.clearInterval(timer);
|
|
panel.schedule=(fn,seconds)=>{timer=h.context.setInterval(()=>{if(panel.node.active) fn.call(panel);},seconds*1000);};
|
|
panel.init=data=>{panel.cacheRefreshPending=false;panel.rendered=data;panel.touchStart=true;panel.initCountdowns(data);};
|
|
home.node=root; home.vibrateButtonClick=home.openLoad2=home.closeLoad=()=>{};
|
|
JiaZai.cachedDailyQuestsPrefab={};
|
|
let instances=0;
|
|
Object.assign(h.context.cc,{isValid:node=>!!node,find:()=>root,instantiate:()=>{instances++;return panel.node;}});
|
|
h.context.cc.director.emit=()=>home.refreshTaskViews();
|
|
h.context.MiniGameSdk={API:{showToast(){}}};
|
|
return {home,panel,red,day,week,get instances(){return instances;}};
|
|
}
|
|
|
|
test('real homepage open/close handlers reuse one popup and only the first load reads the server', () => {
|
|
const h=setup(), ui=installUI(h); ui.home.checkTasks(); ui.home.openDailyQuests(null,'dayBtn');
|
|
assert.equal(h.requests.length,1); h.respond(h.data());
|
|
assert.equal(ui.instances,1);
|
|
for(let i=0;i<4;i++){ui.panel.closeDailyQuests();ui.home.openDailyQuests(null,'dayBtn');}
|
|
assert.equal(h.requests.length,0); assert.equal(ui.instances,1);
|
|
h.Tracker.record({metric:'pass_levels',amount:10}); assert.equal(ui.red.active,true);
|
|
ui.panel.closeDailyQuests(); assert.equal(h.requests.length,0); assert.equal(ui.red.active,true);
|
|
});
|
|
|
|
test('open popup keeps old tasks at midnight and fetches on reopening without resetting a valid countdown', () => {
|
|
const h=setup(), ui=installUI(h); h.load(h.data({dayEndsAt:h.now+2000}));
|
|
ui.home.openDailyQuests(null,'dayBtn'); h.advance(1000); assert.ok(ui.day.text.endsWith('00:00:01'));
|
|
ui.panel.closeDailyQuests();ui.home.openDailyQuests(null,'dayBtn');assert.ok(ui.day.text.endsWith('00:00:01'));
|
|
h.advance(1000); assert.equal(h.requests.length,0); assert.ok(ui.day.text.endsWith('00:00:00'));
|
|
assert.equal(ui.panel.rendered.dayKey,'day1');
|
|
h.advance(60000); assert.equal(h.requests.length,0);
|
|
ui.panel.closeDailyQuests(); assert.equal(h.requests.length,0);
|
|
ui.home.openDailyQuests(); assert.equal(h.requests.length,1);
|
|
h.respond(h.data({dayKey:'day2'})); assert.equal(ui.panel.rendered.dayKey,'day2'); assert.equal(ui.panel.touchStart,true);
|
|
});
|
|
|
|
test('failed refresh on reopening does not poll or reopen the expired popup', () => {
|
|
const h=setup(), ui=installUI(h);h.load(h.data({dayEndsAt:h.now+1000}));ui.home.openDailyQuests();
|
|
h.advance(1000);ui.panel.closeDailyQuests();ui.home.openDailyQuests();
|
|
h.requests.shift().callback({code:0,msg:'offline'});
|
|
assert.equal(ui.panel.node.active,false);h.advance(60000);assert.equal(h.requests.length,0);
|
|
ui.home.openDailyQuests();assert.equal(h.requests.length,1);h.respond(h.data({dayKey:'day2'}));
|
|
assert.equal(ui.panel.node.active,true);assert.equal(ui.panel.rendered.dayKey,'day2');
|
|
});
|
|
|
|
test('background refresh after day or week rollover cannot replace an open popup until reopening', () => {
|
|
for (const end of ['dayEndsAt','weekEndsAt']) {
|
|
const h=setup(),ui=installUI(h);h.load(h.data({[end]:h.now+1000}));ui.home.openDailyQuests();
|
|
ui.panel.cacheRefreshPending=true;
|
|
h.advance(1000);assert.equal(h.requests.length,0);
|
|
h.GameTool.getDailyQuestsInfo(()=>{},true);h.respond(h.data({dayKey:'day2',weekKey:'week2'}));
|
|
assert.equal(ui.panel.rendered.dayKey,'day1');
|
|
h.advance(2000);assert.equal(ui.panel.rendered.dayKey,'day1');
|
|
ui.panel.closeDailyQuests();ui.home.openDailyQuests();
|
|
assert.equal(ui.panel.rendered.dayKey,'day2');assert.equal(h.requests.length,0);
|
|
}
|
|
});
|
|
|
|
test('refresh received during a popup animation is displayed once the animation unlocks', () => {
|
|
const h=setup(),ui=installUI(h);h.load();ui.home.openDailyQuests();ui.panel.touchStart=false;
|
|
h.GameTool.debugDailyTasks('refresh',()=>{});const updated=h.data();updated.weeklyStars=70;h.respond(updated);
|
|
assert.equal(ui.panel.rendered.weeklyStars,0);ui.panel.touchStart=true;h.advance(1000);
|
|
assert.equal(ui.panel.rendered.weeklyStars,70);assert.equal(h.requests.length,0);
|
|
});
|
|
|
|
test('failed debug pause cannot be followed by destructive refresh/resume until the upload succeeds', () => {
|
|
const h=setup(); h.load(); h.Tracker.record({metric:'pass_levels'});
|
|
let result; h.GameTool.debugDailyTasks('pause',r=>result=r);h.requests.shift().callback({code:0,msg:'offline'});
|
|
assert.equal(result.code,0);
|
|
for(const action of ['refresh','resume']){h.GameTool.debugDailyTasks(action,r=>result=r);assert.equal(result.code,0);assert.equal(h.requests.length,0);}
|
|
h.GameTool.debugDailyTasks('pause',r=>result=r);h.ackSave();assert.equal(result.code,1);
|
|
});
|
|
|
|
test('forced debug refresh can reset claimed daily and weekly states after a successful pause', () => {
|
|
const h=setup(), initial=h.data();initial.dailyTasks[0].value=10;initial.dailyTasks[0].state=2;initial.weeklyRewards[0].state=2;
|
|
h.load(initial);h.GameTool.debugDailyTasks('pause',()=>{});h.GameTool.debugDailyTasks('refresh',()=>{});
|
|
const reset=h.data();reset.dailyTasks[0].state=1;reset.dailyTasks[0].value=10;reset.weeklyRewards[0].state=1;h.respond(reset);
|
|
assert.equal(h.info.tasks.dailyTasks[0].state,1);assert.equal(h.info.tasks.weeklyRewards[0].state,1);
|
|
});
|
|
|
|
test('debug pause confirms recovered online progress so a subsequent test reset remains authoritative', () => {
|
|
const h=setup();h.Tracker.startOnline();h.advance(60000);
|
|
const initial=h.data({dailyTasks:[{taskId:'online',metric:'online_seconds',value:0,target:300,state:0}]});h.load(initial);
|
|
assert.equal(h.value(),60);let paused;
|
|
h.GameTool.debugDailyTasks('pause',r=>paused=r);h.ackSave();assert.equal(paused.code,1);
|
|
h.GameTool.debugDailyTasks('refresh',()=>{});h.respond(initial);assert.equal(h.value(),0);
|
|
});
|
|
|
|
test('the first request can obtain its owner UID from storage before Utils copies it into GM_INFO', () => {
|
|
const h=setup();h.info.uid='';h.context.cc.fx.StorageMessage.getStorage=()=> 'stored-user';h.load();
|
|
assert.equal(h.info.uid,'stored-user');assert.ok(h.Sync.cached());
|
|
});
|
|
|
|
test('task transport completes failed requests once so timeouts cannot hold the synchronization queue', () => {
|
|
for(const event of ['ontimeout','onerror','onabort']){
|
|
const h=setup(), xhr={open(){},setRequestHeader(){},send(){}};
|
|
h.context.cc.loader={getXMLHttpRequest:()=>xhr};
|
|
const post=method(utils,'POST').replace('public static POST','POST');
|
|
const compiled=stripTypeScriptTypes(`globalThis.post={${post}}.POST;`);vm.runInNewContext(compiled,h.context);
|
|
h.Utils.POST=h.context.post; h.Utils.testHttpip='https://example.invalid/';h.Utils.httpip=h.Utils.testHttpip;
|
|
const results=[];h.GameTool.getDailyQuestsInfo(r=>results.push(r));xhr[event]();
|
|
xhr.readyState=4;xhr.status=200;xhr.responseText=JSON.stringify({code:1,data:{task:h.data()}});xhr.onreadystatechange();
|
|
assert.equal(results.length,1);assert.equal(results[0].code,0);assert.equal(h.Sync.busy,false);
|
|
}
|
|
});
|