const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const vm = require('node:vm'); const path = require('node:path'); const root = path.join(__dirname, '..'); function formatter() { const context = {module:{exports:{}}, require(){return {getImageUrl:p=>p ? 'media/' + p : ''};}}; vm.runInNewContext(fs.readFileSync(path.join(root,'utils/journal.js'),'utf8'),context); return context.module.exports; } function records(api) { let definition; const routes=[]; vm.runInNewContext(fs.readFileSync(path.join(root,'pages/discover/discover.js'),'utf8'), { Page:p=>definition=p, console:{error(){}}, setTimeout:f=>f(), wx:{getStorageSync:()=> 'token',navigateTo:p=>routes.push(p.url)}, require(name) { if(name.endsWith('/page-cache'))return {withDataCache:p=>p}; if(name.endsWith('/request'))return {api}; if(name.endsWith('/journal'))return formatter(); if(name.endsWith('/layout'))return {getLayout:()=>({})}; return {saveScrollPosition(){},getScrollPosition:()=>0}; } }); return {page:{...definition,data:{...definition.data},setData(v){Object.assign(this.data,v);}},routes}; } test('journal handles both stored image formats, malformed images and text-only entries',()=>{ const {diaryEntry}=formatter(); for(const images of [['one.jpg','two.jpg'],'["one.jpg","two.jpg"]']) { const entry=diaryEntry({id:314,images,content:'a\n b',created_at:'2026-10-04 18:32:00'}); assert.equal(entry.cover,'media/one.jpg');assert.equal(entry.imageCount,2);assert.equal(entry.summary,'a b');assert.equal(entry.dateLabel,'2026.10.04');assert.equal(entry.id,314); } for(const images of ['invalid','{}',null])assert.equal(diaryEntry({images}).cover,''); }); test('records filters use fresh API data and open registered detail/publish destinations',async()=>{ const {page,routes}=records({timelineList:async()=>[{id:3,event_date:'2026-10-12',title:'旅行'}],albumList:async()=>[{id:4,cover:'a.jpg',photo_count:2}],diaryList:async()=>({items:[{id:314,images:'["b.jpg"]'}],total:1})}); await page.loadStats();assert.equal(page.data.entries[0].id,314);assert.equal(page.data.entries[0].cover,'media/b.jpg');assert.equal(page.data.memories[0].dateDay,'12');assert.equal(page.data.albums[0].cover,'media/a.jpg');assert.equal(page.data.loading,false); page.selectFilter({currentTarget:{dataset:{filter:'album'}}});assert.equal(page.data.activeFilter,'album'); page.openJournalEntry({currentTarget:{dataset:{id:314}}});page.openAlbum({currentTarget:{dataset:{id:4}}}); page.showPublish();page.createMemory();assert.equal(page.data.publishOpen,false); assert.deepEqual(routes,['/pages/diary_detail/diary_detail?id=314','/pages/album/album?album_id=4','/pages/publish/publish?type=timeline']); }); test('refresh failure clears busy states and retry replaces old records with authoritative empty data',async()=>{ let fail=true; const {page}=records({timelineList:async()=>[],albumList:async()=>[],diaryList:async()=>{if(fail)throw Error('offline');return {items:[],total:0};}}); page.data.entries=[{id:'old'}];await page.onRefresh();assert.equal(page.data.loadError,true);assert.equal(page.data.refreshing,false);assert.equal(page.data.loading,false); fail=false;await page.loadStats();assert.equal(page.data.loadError,false);assert.equal(page.data.entries.length,0); }); test('publish drawer hides the native custom tab bar and restores it when closing or navigating',()=>{ const {page}=records({});const hidden=[];page.getTabBar=()=>({setHidden:value=>hidden.push(value)}); page.showPublish();assert.equal(page.data.publishOpen,true);assert.equal(hidden.at(-1),true); page.hidePublish();assert.equal(page.data.publishOpen,false);assert.equal(hidden.at(-1),false); page.showPublish();page.onHide();assert.equal(page.data.publishOpen,false);assert.equal(hidden.at(-1),false); page.showPublish();page.createMemory();assert.equal(page.data.publishOpen,false);assert.equal(hidden.at(-1),false); });