Gate record features and storage entry points with invitation guidance; enforce record access in the backend. Add homepage greetings, right-swipe profile dismissal, and move the smoking switch to settings. Simplify API base configuration and remove the unused legacy tab bar component.
57 lines
4.9 KiB
JavaScript
57 lines
4.9 KiB
JavaScript
const test=require('node:test'), assert=require('node:assert/strict'), path=require('node:path');
|
|
const createLoader=require('./load-module.cjs');
|
|
const root=path.join(__dirname,'..');
|
|
function env(cid='') {
|
|
const modals=[],routes=[],storage={coupleId:cid};let page,component;
|
|
const app={globalData:{},ensureAuth:async()=>{}};
|
|
const wx={getStorageSync:k=>storage[k],removeStorageSync:k=>delete storage[k],showModal:o=>modals.push(o),switchTab:o=>routes.push(o.url),navigateTo:o=>routes.push(o.url),showToast(){}};
|
|
const load=createLoader({wx,console,Date,Promise,setTimeout,clearTimeout,setInterval,clearInterval,getApp:()=>app,Page:p=>page=p,Component:c=>component=c,getCurrentPages:()=>[{route:'pages/index/index',data:{}}]}, {'../../utils/request':{api:{},getImageUrl:v=>v},'../../utils/page-cache':{withDataCache:p=>p}});
|
|
return {wx,modals,routes,storage,app,load,page:()=>({...page,data:{...page.data},setData(v){Object.assign(this.data,v)}}),component:()=>component};
|
|
}
|
|
test('unbound entry modal is shared and confirmation opens the existing invitation panel',()=>{
|
|
const e=env(),guard=e.load(path.join(root,'utils/record-access.js'));
|
|
assert.equal(guard.requireRecordAccess(),false);guard.requireRecordAccess();assert.equal(e.modals.length,1);
|
|
const modal=e.modals[0];assert.equal(modal.confirmText,'立即邀请');modal.success({confirm:true});modal.complete();
|
|
assert.equal(e.app.globalData.openProfileBind,true);assert.deepEqual(e.routes,['/pages/profile/profile']);
|
|
e.storage.coupleId='couple';assert.equal(guard.requireRecordAccess(),true);
|
|
});
|
|
test('homepage record links and profile feature shortcuts reject unbound users before navigation',()=>{
|
|
for(const [file,methods] of [['index/index',['showMore','writeDiary','goToAlbum','goToCheckin']],['profile/profile',['openStatistic']]]) {
|
|
for(const method of methods){const e=env();e.load(path.join(root,'pages',file+'.js'));const p=e.page();p[method]({currentTarget:{dataset:{key:'diary'}}});assert.equal(e.routes.length,0);assert.equal(e.modals.length,1,method);}
|
|
}
|
|
});
|
|
test('record tab rejects unbound navigation and allows bound navigation',()=>{
|
|
for(const cid of ['', 'couple']){const e=env(cid);e.load(path.join(root,'custom-tab-bar/index.js'));const c=e.component();const p={...c.methods,setData(){}};p.navigate({currentTarget:{dataset:{tab:'records'}}});assert.equal(e.routes.length,cid?1:0);assert.equal(e.modals.length,cid?0:1);}
|
|
});
|
|
test('all protected direct links avoid loading records when unbound and cancellation returns home',async()=>{
|
|
const pages=['discover/discover','timeline/timeline','punch/punch','diary/diary','albums/albums','album/album','lovewords/lovewords','publish/publish','checkin-detail/checkin-detail','timeline-detail/timeline-detail','period/period','diary_detail/diary_detail','diary_edit/diary_edit','pet/pet','pet/achievements','smoking-detail/smoking-detail','storage/storage'];
|
|
for(const file of pages){const e=env();e.load(path.join(root,'pages',file+'.js'));const p=e.page();await p.onLoad({});await p.onShow();assert.equal(p.data.recordAccessBlocked,true,file);assert.equal(e.modals.length,1,file);e.modals[0].success({cancel:true});e.modals[0].complete();assert.deepEqual(e.routes,['/pages/index/index']);}
|
|
});
|
|
test('cold direct entry waits for login before deciding binding and loading the page',async()=>{
|
|
const e=env();let loaded=0,shown=0,finish;
|
|
e.app.ensureAuth=()=>new Promise(resolve=>finish=()=>{e.storage.coupleId='bound';resolve();});
|
|
const guard=e.load(path.join(root,'utils/record-access.js'));
|
|
const p=guard.withRecordAccess({onLoad(){loaded++;},onShow(){shown++;},setData(v){this.data={...this.data,...v};}});
|
|
const load=p.onLoad(),show=p.onShow();assert.equal(loaded,0);finish();await Promise.all([load,show]);assert.equal(loaded,1);assert.equal(shown,1);assert.equal(p.data.recordAccessBlocked,false);assert.equal(e.modals.length,0);
|
|
e.storage.coupleId='';await p.onShow();assert.equal(shown,1);assert.equal(e.modals.length,1);
|
|
});
|
|
|
|
test('authoritative backend denial clears stale binding and offers invitation',async()=>{
|
|
const e=env('stale-couple');
|
|
e.wx.request=o=>o.success({statusCode:403,data:{error:'请先绑定情侣,再使用记录功能',code:10002}});
|
|
const {api}=e.load(path.join(root,'utils/request.js'));
|
|
await assert.rejects(api.diaryAdd({content:'blocked'}),error=>error.code===10002);
|
|
assert.equal(e.storage.coupleId,undefined);assert.equal(e.modals.length,1);assert.equal(e.routes.length,0);
|
|
});
|
|
|
|
test('more and storage entries require binding while bound navigation stays available',()=>{
|
|
for(const cid of ['', 'couple']) {
|
|
for(const method of ['openStatistic','goStorage','goToSettings']) {
|
|
const e=env(cid);e.load(path.join(root,'pages/profile/profile.js'));const p=e.page();
|
|
p[method]({currentTarget:{dataset:{key:'settings'}}});
|
|
assert.equal(e.modals.length,cid?0:1,method);assert.equal(e.routes.length,cid?1:0,method);
|
|
if(cid) assert.equal(e.routes[0],method==='goStorage'?'/pages/storage/storage':'/pages/settings/settings');
|
|
}
|
|
}
|
|
});
|