Add server and mini-program pagination for shared lists
This commit is contained in:
@@ -15,6 +15,7 @@ function records(api) {
|
||||
Page:p=>definition=p, console:{error(){}}, setTimeout:f=>f(),
|
||||
wx:{getStorageSync:()=> 'token',navigateTo:p=>routes.push(p.url)},
|
||||
require(name) {
|
||||
if(name.endsWith('/list-pagination'))return require('../utils/list-pagination');
|
||||
if(name.endsWith('/page-cache'))return {withDataCache:p=>p};
|
||||
if(name.endsWith('/request'))return {api};
|
||||
if(name.endsWith('/journal'))return formatter();
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
const test=require('node:test');
|
||||
const assert=require('node:assert/strict');
|
||||
const path=require('node:path');
|
||||
const helpers=require('../utils/list-pagination');
|
||||
const envelope=(items,total=42,has_more=true)=>({items,total,has_more});
|
||||
function page(file,api){
|
||||
let definition;
|
||||
const load=require('./load-module.cjs')({Page:p=>definition=p,wx:{getStorageSync:()=>1,showToast(){}},console,Date,Promise,setTimeout},{'../../utils/request':{api,getImageUrl:v=>v},'../../utils/page-cache':{withDataCache:p=>p}});
|
||||
load(path.join(__dirname,'../pages',file));
|
||||
return {...definition,data:{...definition.data},setData(values){Object.assign(this.data,values);}};
|
||||
}
|
||||
test('songs append once, preserve full total, and retry the failed page',async()=>{
|
||||
let fail=true;const calls=[];
|
||||
const p=page('lovewords/lovewords.js',{songsList:async({page,page_size})=>{calls.push(page);assert.equal(page_size,20);if(page===1)return envelope([{id:1}]);if(fail)throw Error('offline');return envelope([{id:1},{id:2}],42,false);}});
|
||||
await p.loadData();assert.equal(p.data.total,42);
|
||||
const originalError=console.error;console.error=()=>{};
|
||||
try {await p.loadMore();}finally{console.error=originalError;}
|
||||
assert.equal(p.data.page,1);assert.equal(p.data.list.length,1);assert.equal(p.data.moreError,true);
|
||||
fail=false;await p.loadMore();assert.deepEqual(calls,[1,2,2]);assert.equal(p.data.list.length,2);assert.equal(p.data.hasMore,false);
|
||||
await p.loadMore();assert.equal(calls.length,3);
|
||||
});
|
||||
test('refresh invalidates an in-flight next page',()=>{
|
||||
const p={data:{list:[]},setData(v){Object.assign(this.data,v);}};
|
||||
let t=helpers.startList(p,1);helpers.applyList(p,t,envelope([{id:1}]),[{id:1}],'list');helpers.finishList(p,t);
|
||||
const old=helpers.startList(p,2);t=helpers.startList(p,1);
|
||||
helpers.applyList(p,t,envelope([{id:9}]),[{id:9}],'list');helpers.finishList(p,t);
|
||||
helpers.applyList(p,old,envelope([{id:2}]),[{id:2}],'list');helpers.finishList(p,old);
|
||||
assert.deepEqual(p.data.list,[{id:9}]);assert.equal(p.data.page,1);
|
||||
});
|
||||
test('album list appends the next page instead of loading all albums',async()=>{
|
||||
const calls=[];const p=page('albums/albums.js',{albumList:async({page,page_size})=>{calls.push([page,page_size]);return envelope([{id:page,title:'album'}],21,page===1);}});
|
||||
await p.loadAlbums();await p.loadMore();assert.deepEqual(calls,[[1,20],[2,20]]);assert.equal(p.data.albums.length,2);assert.equal(p.data.total,21);
|
||||
});
|
||||
test('photos crossing page boundaries stay in the same upload card and retain saved metadata',async()=>{
|
||||
const calls=[];const p=page('album/album.js',{photos:async(id,{page,page_size})=>{calls.push([id,page,page_size]);return envelope([{id:page,batch_id:'same',created_by:1,path:page+'.jpg',created_at:'2026-10-11',location:page===1?'old':'second'}],2,page===1);},coupleInfo:async()=>({partner1:{id:1,nickname:'我'},partner2:{id:2,nickname:'TA'}}),photoUpdate:async()=>({success:true})});
|
||||
p.data.albumId=8;await p.loadData();
|
||||
p.data.editPhotos=p.data.batches[0].photos;p.data.editForm={taken_at:'',location:'saved',activity:''};await p.savePhotoInfo();
|
||||
await p.loadMore();assert.deepEqual(calls,[[8,1,20],[8,2,20]]);assert.equal(p.data.batches.length,1);assert.equal(p.data.batches[0].photos.length,2);assert.equal(p.data.batches[0].photos[0].location,'saved');assert.equal(p.data.hasMore,false);
|
||||
});
|
||||
@@ -38,7 +38,7 @@ test('failed homepage load ends the recent-record spinner',async()=>{
|
||||
let definition;
|
||||
const api={coupleInfo:async()=>{throw { _statusCode:404 };}};
|
||||
vm.runInNewContext(fs.readFileSync(path.join(__dirname,'../pages/index/index.js'),'utf8'),{
|
||||
Page(p){definition=p;},require(name){return name.endsWith('/page-cache')?{withDataCache:p=>p}:name.endsWith('/request')?{api,getImageUrl:v=>v}:name.endsWith('/layout')?{getLayout:()=>({})}:{saveScrollPosition(){},getScrollPosition(){return 0;}};},
|
||||
Page(p){definition=p;},require(name){return name.endsWith('/list-pagination')?require('../utils/list-pagination'):name.endsWith('/page-cache')?{withDataCache:p=>p}:name.endsWith('/request')?{api,getImageUrl:v=>v}:name.endsWith('/layout')?{getLayout:()=>({})}:{saveScrollPosition(){},getScrollPosition(){return 0;}};},
|
||||
wx:{getStorageSync:()=> 'test-token'}, console:{error(){}}, Date, setTimeout
|
||||
});
|
||||
const page={...definition,data:{...definition.data},setData(data){Object.assign(this.data,data);}};
|
||||
@@ -50,7 +50,7 @@ test('unbound newcomer loads homepage records without requesting couple-only pet
|
||||
let definition;let petCalls=0;
|
||||
const api={coupleInfo:async()=>({not_bound:true,partner1:{nickname:'新用户'}}),timelineList:async()=>[],albumList:async()=>[],diaryList:async()=>({items:[],total:0}),songsList:async()=>[],checkinList:async()=>({items:[]}),petInfo:async()=>{petCalls++;throw Error('请先绑定情侣');}};
|
||||
vm.runInNewContext(fs.readFileSync(path.join(__dirname,'../pages/index/index.js'),'utf8'),{
|
||||
Page(p){definition=p;},require(name){return name.endsWith('/page-cache')?{withDataCache:p=>p}:name.endsWith('/request')?{api,getImageUrl:v=>v}:name.endsWith('/layout')?{getLayout:()=>({})}:name.endsWith('/journal')?{diaryEntry:v=>v}:{};},
|
||||
Page(p){definition=p;},require(name){return name.endsWith('/list-pagination')?require('../utils/list-pagination'):name.endsWith('/page-cache')?{withDataCache:p=>p}:name.endsWith('/request')?{api,getImageUrl:v=>v}:name.endsWith('/layout')?{getLayout:()=>({})}:name.endsWith('/journal')?{diaryEntry:v=>v}:{};},
|
||||
wx:{getStorageSync:()=>1,removeStorageSync(){}},console,Date,setTimeout
|
||||
});
|
||||
const page={...definition,data:{...definition.data,coupleId:99},setData(data){Object.assign(this.data,data);}};
|
||||
|
||||
Reference in New Issue
Block a user