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.
48 lines
6.5 KiB
JavaScript
48 lines
6.5 KiB
JavaScript
const test=require('node:test');const assert=require('node:assert/strict');const path=require('node:path');const loader=require('./load-module.cjs');
|
|
function page(name,api,wxExtra={},app={}){let definition;const storage=new Map([['userId','user']]);const dialogs=[],toasts=[];const wx={getStorageSync:k=>storage.get(k),setStorageSync:(k,v)=>storage.set(k,v),removeStorageSync:k=>storage.delete(k),showModal:o=>dialogs.push(o),showToast:o=>toasts.push(o),showLoading(){},hideLoading(){},reLaunch(){},...wxExtra};loader({Page:p=>definition=p,wx,getApp:()=>app,console,Date,Promise,setTimeout,clearTimeout},{'../../utils/request':{api,getImageUrl:v=>v},'../../utils/page-cache':{withDataCache:p=>p},'../../utils/layout':{getLayout:()=>({})}})(path.join(__dirname,'../pages',name));return {p:{...definition,data:JSON.parse(JSON.stringify(definition.data)),setData(v){Object.assign(this.data,v);}},storage,dialogs,toasts};}
|
|
const flush=async()=>{for(let i=0;i<30;i++)await Promise.resolve();};
|
|
test('smoking preference is restored from server on a newly created page, even without local storage',async()=>{
|
|
let visible=0;const api={checkAuth:async()=>({user:{smoking_card_visible:visible}}),smokingCardVisible:async d=>{visible=d.visible;return {card_visible:visible};}};
|
|
const first=page('settings/settings.js',api);await first.p.loadSmokingPreference();assert.equal(first.p.data.smokingVisible,false);await first.p.toggleSmoking({detail:{value:true}});assert.equal(visible,1);
|
|
const installed=page('punch/punch.js',api);await installed.p.loadSmokingPreference();assert.equal(installed.p.data.smokingVisible,true);assert.equal(installed.storage.has('smokingVisible'),false);
|
|
});
|
|
test('failed smoking preference save retains the last server state',async()=>{
|
|
const e=page('settings/settings.js',{checkAuth:async()=>({user:{smoking_card_visible:0}}),smokingCardVisible:async()=>{throw {error:'offline'};}});await e.p.loadSmokingPreference();await e.p.toggleSmoking({detail:{value:true}});assert.equal(e.p.data.smokingVisible,false);assert.equal(e.p.data.smokingSaving,false);
|
|
});
|
|
test('announcement is dismissible per revision and server disable immediately hides it',async()=>{
|
|
let data={enabled:true,title:'公告',content:'Hello',revision:'v1'};const e=page('index/index.js',{announcement:async()=>data});await e.p.loadAnnouncement();assert.equal(e.p.data.showAnnouncement,true);e.p.closeAnnouncement();await e.p.loadAnnouncement();assert.equal(e.p.data.showAnnouncement,false);data={...data,content:'Updated',revision:'v2'};await e.p.loadAnnouncement();assert.equal(e.p.data.showAnnouncement,true);data={...data,enabled:false};await e.p.loadAnnouncement();assert.equal(e.p.data.showAnnouncement,false);
|
|
});
|
|
test('account deletion does not submit when either confirmation is cancelled',async()=>{
|
|
for(const cancelAt of [0,1]){let deleted=0;const e=page('settings/settings.js',{accountDeletionPreview:async()=>({summary:{counts:{files:2,photos:1}},confirmation_token:'proof'}),accountDelete:async()=>{deleted++;}});const pending=e.p.deleteAccount();await flush();assert.equal(e.dialogs.length,1);e.dialogs[0].success({confirm:cancelAt!==0});await flush();if(cancelAt===1)e.dialogs[1].success({confirm:false});await pending;assert.equal(deleted,0);assert.equal(e.p._deletingAccount,false);}
|
|
});
|
|
test('account deletion submits the server proof only after both confirmations and clears the session',async()=>{
|
|
let payload,cleared=false;const e=page('settings/settings.js',{accountDeletionPreview:async()=>({summary:{counts:{files:2,photos:1}},confirmation_token:'proof'}),accountDelete:async d=>{payload=d;return {success:true};}},{},{finishAccountDeletion(){cleared=true;}});const pending=e.p.deleteAccount();await flush();e.dialogs[0].success({confirm:true});await flush();assert.equal(payload,undefined);e.dialogs[1].success({confirm:true});await pending;assert.equal(payload.confirmed,true);assert.equal(payload.confirmation_token,'proof');assert.equal(cleared,true);
|
|
});
|
|
|
|
test('announcement login-once mode resets only with a new runtime session and isolates accounts',()=>{
|
|
const create=()=>loader({}) (path.join(__dirname,'../utils/announcement-session.js'));
|
|
const session=create(),notice={enabled:true,mode:'login_once',content:'Hello',revision:'v1'};
|
|
assert.equal(session.shouldShow(notice,'a'),true);session.markShown(notice,'a');assert.equal(session.shouldShow(notice,'a'),false);assert.equal(session.shouldShow(notice,'b'),true);assert.equal(create().shouldShow(notice,'a'),true);
|
|
session.clear();assert.equal(session.shouldShow(notice,'a'),true);
|
|
});
|
|
test('always mode returns on each homepage entry while off and blank content never display',async()=>{
|
|
let data={enabled:true,mode:'always',content:'Hello',revision:'v1'};const e=page('index/index.js',{announcement:async()=>data});
|
|
await e.p.loadAnnouncement();assert.equal(e.p.data.showAnnouncement,true);e.p.closeAnnouncement();await e.p.loadAnnouncement();assert.equal(e.p.data.showAnnouncement,true);
|
|
data={...data,mode:'off'};await e.p.loadAnnouncement();assert.equal(e.p.data.showAnnouncement,false);
|
|
data={...data,mode:'always',content:' \n '};await e.p.loadAnnouncement();assert.equal(e.p.data.showAnnouncement,false);
|
|
});
|
|
|
|
test('concurrent homepage notice reads share a request and do not dismiss a fresh popup',async()=>{
|
|
let done,calls=0;const e=page('index/index.js',{announcement:()=>{calls++;return new Promise(resolve=>done=resolve);}});const a=e.p.loadAnnouncement(),b=e.p.loadAnnouncement();assert.equal(calls,1);done({mode:'login_once',enabled:true,content:'hello',revision:'v1'});await Promise.all([a,b]);assert.equal(e.p.data.showAnnouncement,true);
|
|
});
|
|
test('a hidden homepage cannot consume a once-per-login notification',async()=>{
|
|
let done;const e=page('index/index.js',{announcement:()=>new Promise(resolve=>done=resolve)});const a=e.p.loadAnnouncement();e.p.onHide();done({mode:'login_once',enabled:true,content:'hello',revision:'v1'});await a;assert.equal(e.p.data.showAnnouncement,false);e.p._announcementActive=true;const b=e.p.loadAnnouncement();done({mode:'login_once',enabled:true,content:'hello',revision:'v1'});await b;assert.equal(e.p.data.showAnnouncement,true);
|
|
});
|
|
|
|
test('bound accounts see an explanation instead of starting account deletion',async()=>{
|
|
let calls=0;const e=page('settings/settings.js',{accountDeletionPreview:async()=>{calls++;},accountDelete:async()=>{calls++;}});
|
|
e.p.data.hasPartner=true;e.p.data.bindingKnown=true;
|
|
await e.p.deleteAccount();assert.equal(calls,0);assert.equal(e.dialogs.length,1);
|
|
assert.match(e.dialogs[0].content,/请先完成情侣解绑/);assert.equal(e.dialogs[0].showCancel,false);
|
|
});
|