Files
web_hgdjc_h5/tests/site-navigation.test.mjs
2026-11-06 00:13:27 +07:00

55 lines
5.4 KiB
JavaScript

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 code=ts.transpileModule(await readFile(new URL(file,import.meta.url),'utf8'),{compilerOptions:{target:ts.ScriptTarget.ES2022,module:ts.ModuleKind.ESNext}}).outputText;
return 'data:text/javascript;base64,'+Buffer.from(replace(code)).toString('base64');
}
const routing=await moduleUrl('../src/lib/routing.ts'),protocol=await moduleUrl('../src/lib/api/protocol.ts');
const {navigationEntries,navigationIsActive,mobileNavigationEntries,withoutRemovedNavigation}=await import(await moduleUrl('../src/lib/site-navigation.ts',code=>code.replace("'./api/protocol'",JSON.stringify(protocol)).replace("'./routing'",JSON.stringify(routing))));
const item=(id,name,urlValue,extra={})=>({id,name,urlValue,categoryType:1,...extra});
test('API order, labels, icons and nested destinations are preserved while footer and removed video entries stay absent',()=>{
const entries=navigationEntries({categoryList:[null,item(20,'精选','/topics/'),item(21,'目录','',{categoryType:2,translationList:[{id:22,name:'第二页',urlValue:'/shorts/page/2/'},null]}),item(23,'更多精彩','/shorts/',{icon:'/assets/custom.webp'}),item(24,'页脚','/info/about-us/',{categoryType:3}),item(25,'剧集','/series/'),item(26,'电影','/video/123/')]});
assert.deepEqual(entries.map(entry=>entry.name),['精选','目录','更多精彩']);assert.equal(entries[0].href,'/topics/');assert.equal(entries[1].children[0].href,'/shorts/page/2/');assert.equal(entries[2].icon,'/assets/custom.webp');
assert.deepEqual(mobileNavigationEntries(entries).map(entry=>entry.name),['精选','第二页','更多精彩']);
for(const categoryList of [null,[],{},'bad',[null]])assert.deepEqual(navigationEntries({categoryList}),[]);
});
test('known API aliases and empty system entries generate native destinations directly without redirects',()=>{
const entries=navigationEntries({categoryList:[item(1,'首页',''),item(2,'排行','/hot'),item(3,'回家的路',''),item(4,'福利应用',''),item(5,'名字由后台决定','/shorts?sort=2'),item(6,'未知空地址','')]});
assert.deepEqual(entries.map(entry=>entry.href),['/','/rank/hot/','/home-road/','/shorts/?sort=2']);
});
test('backend navigation entries for the removed welfare app directory are filtered out',()=>{
const entries=navigationEntries({categoryList:[item(1,'短剧','/shorts/'),item(2,'应用','/apps/'),item(3,'应用二','/application'),item(4,'福利应用','https://drama.test/apps/'),item(5,'福利应用','/topics/'),item(6,'目录','',{categoryType:2,translationList:[{id:7,name:'应用',urlValue:'/apps/'},{id:8,name:'专题',urlValue:'/topics/'}]})]},'https://drama.test');
assert.deepEqual(entries.map(entry=>entry.name),['短剧','目录']);
assert.deepEqual(entries[1].children.map(entry=>entry.href),['/topics/']);
assert.ok(!JSON.stringify(entries).includes('/apps'));
});
test('links reject executable addresses, credentials and removed routes, and keep external URLs external',()=>{
const entries=navigationEntries({categoryList:[item(1,'外部','https://example.net/path'),item(2,'本站','https://drama.test/shorts'),...['javascript:alert(1)','data:text/html,x','https://user:pass@example.net/','/\\example.net','/video/7/','/series','/shorts/#bad'].map((url,index)=>item(10+index,'无效',url))]},'https://drama.test');
assert.equal(entries.length,2);assert.equal(entries[0].external,true);assert.equal(entries[1].external,false);assert.equal(entries[1].href,'/shorts/');
});
test('active navigation follows configured destinations, independent pages, pagination and query filters',()=>{
assert.equal(navigationIsActive('/','/'),true);assert.equal(navigationIsActive('/','/shorts/'),false);
assert.equal(navigationIsActive('/shorts/','/shorts/page/3/'),true);
assert.equal(navigationIsActive('/shorts/','/detail/7/','top'),false);assert.equal(navigationIsActive('/shorts/','/detail/7/','sidebar'),true);
assert.equal(navigationIsActive('/topics/','/topics/7/','top'),false);assert.equal(navigationIsActive('/topics/7/','/topics/7/page/2/'),true);assert.equal(navigationIsActive('/topics/7/','/topics/8/'),false);
for(const slug of ['hot','new','follow'])assert.equal(navigationIsActive('/rank/hot/',`/rank/${slug}/page/2/`),true);
assert.equal(navigationIsActive('/rank/new/','/rank/follow/'),false);
assert.equal(navigationIsActive('/shorts/?sort=2','/shorts/page/2/?sort=2&campaign=test'),true);assert.equal(navigationIsActive('/shorts/?sort=2','/shorts/?sort=1'),false);
assert.equal(navigationIsActive('https://example.net/shorts/','/shorts/'),false);assert.equal(navigationIsActive('/series/','/series/'),false);
});
test('the serialized backend navigation payload no longer carries welfare-app entries',()=>{
const data=withoutRemovedNavigation({categoryList:[{id:1,name:'短剧',urlValue:'/shorts/',categoryType:1},{id:2,name:'福利应用',urlValue:'',categoryType:1},{id:3,name:'应用',urlValue:'https://drama.test/apps/',categoryType:1},{id:4,name:'目录',urlValue:'',categoryType:2,translationList:[{id:5,name:'应用',urlValue:'/application'},{id:6,name:'专题',urlValue:'/topics/'}]}]});
assert.deepEqual(data.categoryList.map(item=>item.id),[1,4]);
assert.deepEqual(data.categoryList[1].translationList.map(item=>item.id),[6]);
assert.deepEqual(withoutRemovedNavigation(null).categoryList,[]);
});