import {routingModuleUrl} from './helpers/routing.mjs'; import {test} from 'node:test'; import assert from 'node:assert/strict'; import {readFile} from 'node:fs/promises'; import ts from 'typescript'; async function moduleUrl(file,replace=code=>code){ const filename=new URL(file,import.meta.url); const {outputText}=ts.transpileModule(await readFile(filename,'utf8'),{compilerOptions:{target:ts.ScriptTarget.ES2022,module:ts.ModuleKind.ESNext}}); return 'data:text/javascript;base64,'+Buffer.from(replace(outputText).replace(/(['"])\.\.\/routing\1/g,JSON.stringify(routingModuleUrl))+'\n//# sourceURL='+filename.pathname).toString('base64'); } const protocol=await moduleUrl('../src/lib/api/protocol.ts'); const render=await moduleUrl('../src/lib/client/render.ts',code=>code.replace('"../api/protocol"',JSON.stringify(protocol))); const apps=await moduleUrl('../src/lib/client/apps.ts',code=>code.replace("'../api/protocol'",JSON.stringify(protocol)).replace("'./render'",JSON.stringify(render))); const {loadAppCategories,renderAppDirectory,scrollToAppCategory}=await import(apps); const app=id=>({id,name:'应用 '+id,url:'https://example.com/app/'+id,logo:'https://example.com/icon.png'}); const category=(id,appList)=>({id,title:'分类 '+id,...(appList===undefined?{}:{appList})}); test('benefit applications report linked advertisements with their API ids and ordinary applications as content',()=>{ const html=renderAppDirectory([category(3,[app(11),{...app(12),advitiseId:'ad-12',location:8}])]); assert.match(html,/data-track-item data-item-id="11" data-item-name="应用 11" data-item-type="app" data-parent-item-id="3"/); assert.match(html,/data-track-ad="external" data-ad-id="ad-12" data-ad-clickable="1" data-ad-slot-key="8" data-ad-slot-name="福利应用" data-ad-type="icon"/); }); test('loads every missing category once, preserves directory order, and omits empty categories',async()=>{ const calls=[],controller=new AbortController(); const api={async get(path,query,signal){ assert.equal(path,'applist/list');assert.equal(signal,controller.signal);calls.push(query.categoryId); if(query.categoryId===0)return {categoryList:[category(2,[]),category(1),category(3,[]),category(4),category(2)],category:category(1,[app(11)])}; return {category:category(query.categoryId,query.categoryId===2?[app(22)]:[])}; }}; const result=await loadAppCategories(api,controller.signal); assert.deepEqual(calls,[0,2,3,4]);assert.deepEqual(result.map(item=>item.id),[2,1]); const html=renderAppDirectory(result); for(const id of [2,1]){assert.ok(html.includes(`aria-controls="apps-${id}"`));assert.ok(html.includes(`id="apps-${id}"`));} for(const id of [3,4]){assert.ok(!html.includes(`data-app-category="${id}"`));assert.ok(!html.includes(`id="apps-${id}"`));} assert.equal((html.match(/class="benefit-section"/g)||[]).length,2); }); test('uses embedded applications without additional requests and escapes API text and links',async()=>{ let calls=0; const result=await loadAppCategories({async get(){calls++;return {categoryList:[{...category(7,[{...app(1),name:'应用