Files
sundynix-pets/tests/miniprogram-breeds-refresh.test.cjs
Blizzard 9d7cf720a5
ci-cd / verify (push) Successful in 1m59s
ci-cd / deploy (push) Skipped
feat: add multi-source breed encyclopedia and two-hour refresh sessions
2026-10-05 13:38:24 +08:00

52 lines
5.4 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
const tick = () => new Promise(resolve => setImmediate(resolve));
function authRuntime() {
let app;
const storage=new Map(), requests=[], logins=[];
const wx={ getStorageSync:k=>storage.get(k), setStorageSync:(k,v)=>storage.set(k,v), removeStorageSync:k=>storage.delete(k), login:o=>logins.push(o), request:o=>requests.push(o) };
const module={exports:{}};
vm.runInNewContext(fs.readFileSync('pets-fe/utils/api.js','utf8'), {module,wx,getApp:()=>app,require:()=>({baseURL:'https://test/api'})});
vm.runInNewContext(fs.readFileSync('pets-fe/app.js','utf8'), {wx,App:value=>app=value,require:()=>module.exports});
const reply=(req,data,statusCode=200)=>req.success({statusCode,data:statusCode===200?{code:0,data}:{message:'expired'}});
return {app,storage,requests,logins,api:module.exports,reply};
}
test('临近到期先刷新,启动与多个业务请求共用一次刷新',async()=>{
const r=authRuntime();r.storage.set('pet_token','old');r.storage.set('pet_token_expires_at',Date.now()/1000+30);r.storage.set('pet_refresh_token','refresh-old');
r.app.onLaunch();const pending=[r.api.secure('/pets'),r.api.secure('/tasks')];await tick();
assert.equal(r.requests.length,1);assert.equal(r.requests[0].url,'https://test/api/auth/refresh');assert.equal(r.logins.length,0);
r.reply(r.requests[0],{token:'new',refresh_token:'refresh-new',expires_at:Date.now()/1000+7200});await tick();
for(const req of r.requests.slice(1)){assert.equal(req.header.Authorization,'Bearer new');r.reply(req,'ok');}
assert.deepEqual(await Promise.all(pending),['ok','ok']);assert.equal(r.storage.get('pet_refresh_token'),'refresh-new');
});
test('刷新过期才回到微信登录,网络错误保留刷新凭证以供重试',async()=>{
const r=authRuntime();r.storage.set('pet_refresh_token','expired');const first=r.app.login();r.reply(r.requests[0],null,401);await tick();
assert.equal(r.logins.length,1);r.logins[0].success({code:'new-code'});await tick();r.reply(r.requests[1],{token:'new',refresh_token:'new-refresh',expires_at:Date.now()/1000+7200});await first;
r.app.globalData.token='';const retry=r.app.login();r.requests[2].fail();await assert.rejects(retry,/网络/);assert.equal(r.storage.get('pet_refresh_token'),'new-refresh');assert.equal(r.logins.length,1);
const last=r.app.login();r.reply(r.requests[3],{token:'last',refresh_token:'last-refresh',expires_at:Date.now()/1000+7200});await last;
assert.equal(r.app.globalData.token,'last');
});
test('品种按 A–Z 分组,支持别名、拼音和首字母搜索',()=>{
const module={exports:{}};vm.runInNewContext(fs.readFileSync('pets-fe/utils/breeds.js','utf8'),{module,require:()=>({secure:()=>{}})});
const rows=[{id:'k',name:'柯基',aliases:'小短腿',pinyin:'keji',pinyin_initials:'kj',initial:'K'},{id:'b',name:'边境牧羊犬',aliases:'边牧',pinyin:'bianjingmuyangquan',pinyin_initials:'bjm yq'.replace(/ /g,''),initial:'B'}];
const group=module.exports.groupBreeds;assert.equal(group(rows)[0].letter,'B');for(const q of ['柯基','小短腿','keji','KJ']) assert.equal(group(rows,q)[0].items[0].id,'k');assert.equal(group(rows,'没有这种犬').length,0);
});
test('手动填写清除品种 ID,保存宠物发送自定义品种',async()=>{
let definition;vm.runInNewContext(fs.readFileSync('pets-fe/components/breed-picker/index.js','utf8'),{require:()=>({}),Component:c=>definition=c,wx:{showToast:()=>{}}});
let change;const component={...definition.methods,data:{custom:' 地方犬 ',visible:true},setData:v=>Object.assign(component.data,v),triggerEvent:(_,v)=>change=v};component.useCustom();assert.equal(change.id,null);assert.equal(change.name,'地方犬');assert.equal(component.data.visible,false);
let pageDef,body;vm.runInNewContext(fs.readFileSync('pets-fe/pages/pet-edit/pet-edit.js','utf8'),{Page:p=>pageDef=p,require:()=>({secure:async(_p,_m,b)=>body=b}),wx:{navigateBack:()=>{}}});
const page={...pageDef,data:{...pageDef.data,name:'小狗',breed_id:'old-id'},setData:v=>Object.assign(page.data,v)};page.breedChange({detail:change});await page.save();assert.equal(body.breed_id,null);assert.equal(body.breed,'地方犬');
page.breedChange({detail:{id:'selected-id',name:'柯基'}});await page.save();assert.equal(body.breed_id,'selected-id');assert.equal(body.breed,'柯基');
});
test('百科详情展示各字段出处,只复制用户点击的来源链接',async()=>{
let definition, copied;
const breed={name:'柯基',origin:'英国',traits:'{"运动活力":4,"无效":9}',exercise:'每天走走',field_sources:{origin:'fci',traits:'akc',exercise:'rkc'},references:[{id:'r1',provider:'fci',url:'https://www.fci.be/en/nomenclature/',retrieved_at:'2026-10-05'}]};
vm.runInNewContext(fs.readFileSync('pets-fe/pages/breed-detail/breed-detail.js','utf8'),{Page:p=>definition=p,require:()=>({secure:async()=>breed}),wx:{setClipboardData:options=>copied=options.data,showToast:()=>{}}});
const page={...definition,data:{...definition.data},id:'breed-id',setData:patch=>Object.assign(page.data,patch)};
await page.load();assert.equal(page.data.facts.find(f=>f.key==='origin').source,'FCI');assert.equal(page.data.traits.length,1);assert.equal(page.data.traitSource,'AKC');assert.equal(page.data.exerciseSource,'英国皇家犬业俱乐部');
page.source({currentTarget:{dataset:{id:'r1'}}});assert.equal(copied,breed.references[0].url);
});