import React from 'react'; import {test} from 'node:test'; import assert from 'node:assert/strict'; import {readFile} from 'node:fs/promises'; import {createHash} from 'node:crypto'; import path from 'node:path'; import sharp from 'sharp'; import {renderToStaticMarkup} from 'react-dom/server'; import {transformSync} from 'esbuild'; import manifest from '../src/content/layouts/reference/manifest.json'; import ReferenceLayout,{layoutIds,validateReplacements} from '../src/content/layouts/reference/ReferenceLayout'; import {textMergeTarget} from '../src/content/layouts/reference/group-text'; import {layoutDocuments,htmlExample,reactExample,nextExample,legacyLayoutIds} from '../src/content/layouts/documents'; import {getServerSideProps} from '../src/pages/explore/[id]'; const publicRoot=path.resolve(import.meta.dirname,'../public'); const asset=(url:string)=>path.join(publicRoot,url); test('27 source layouts have stable row-major IDs, 5/5/5/6/6 positions and scoped components',async()=>{ assert.deepEqual(manifest.map(l=>l.id),[...layoutIds]);assert.equal(layoutDocuments.length,28); assert.deepEqual([1,2,3,4,5].map(row=>manifest.filter(l=>l.source.row===row).length),[5,5,5,6,6]); const hash=createHash('sha256').update(await readFile(asset('/layout-reference/source.png'))).digest('hex'); for(const l of manifest){assert.equal(l.source.sha256,hash);assert.equal(new Set(l.elements.map(e=>e.id)).size,l.elements.length);for(const e of l.elements){assert.ok(e.asset.startsWith('/layout-reference/'+l.id+'/'));assert.ok(e.pixels>0);assert.equal(e.id[0],{text:'T',image:'I',decoration:'D'}[e.kind]);if(e.reading==='unreadable')assert.equal(e.text,null)}} }); test('all 27 extracted layer composites equal their original source crops at every RGB pixel',async()=>{ for(const l of manifest){ const result=await sharp(asset(l.background)).composite(l.elements.map(e=>({input:asset(e.asset),left:e.box[0],top:e.box[1]}))).removeAlpha().raw().toBuffer(); const reference=await sharp(asset(l.reference)).removeAlpha().raw().toBuffer();assert.ok(result.equals(reference),l.id+' physical layer composite must match'); } }); test('a residual end glyph stays in T01 instead of creating a second word; other columns stay independent',()=>{ assert.equal(textMergeTarget([38,24,6,6],[{box:[2,20,38,15],direction:'horizontal'}]),0); assert.equal(textMergeTarget([90,24,6,6],[{box:[2,20,38,15],direction:'horizontal'}]),-1); const first=manifest[0];assert.deepEqual(first.elements.filter(e=>e.kind==='text').map(e=>e.id),['T01','T02']); assert.ok(first.elements.find(e=>e.id==='T01')!.box[0]+first.elements.find(e=>e.id==='T01')!.box[2]>=44); const date=manifest.find(l=>l.id==='ref-21')!.elements.find(e=>e.id==='T03')!;assert.equal(date.text,'03');assert.equal(date.kind,'text'); const grayWord=manifest.find(l=>l.id==='ref-23')!.elements.find(e=>e.text==='BULUBLUE')!;assert.equal(grayWord.kind,'text'); }); test('component replacement rejects foreign IDs and wrong kinds; default React renders exact source assets',()=>{ assert.throws(()=>validateReplacements('ref-01',{text:{'ref-02/T01':'wrong'}})); assert.throws(()=>validateReplacements('ref-01',{text:{'ref-01/I01':'wrong'}})); assert.throws(()=>validateReplacements('missing',{})); for(const id of layoutIds){const html=renderToStaticMarkup();assert.ok(html.includes('data-layout-id="'+id+'"'));assert.equal((html.match(/data-component=/g)||[]).length,manifest.find(l=>l.id===id)!.elements.length)} const edited=renderToStaticMarkup();assert.ok(edited.includes('我的品牌'));assert.ok(edited.includes('mask-image'));assert.ok(edited.includes('T01-underlay.png')); }); test('HTML, React and Next.js examples bind each source ID and retired approximate posts redirect to Router',async()=>{ for(const l of manifest){assert.ok(htmlExample(l.id).includes("const id='"+l.id+"'"));transformSync(reactExample(l.id),{loader:'tsx'});transformSync(nextExample(l.id),{loader:'tsx'});const html=await readFile(asset('/layout-reference/'+l.id+'/index.html'),'utf8');assert.ok(html.includes('data-toggle="text"'));assert.ok(html.includes('data-toggle="image"'));assert.ok(html.includes('manifest='+JSON.stringify(l).replaceAll('<','\\u003c')))} for(const id of legacyLayoutIds){const response=await getServerSideProps({params:{id},query:{},res:{},req:{}} as never);assert.ok('redirect' in response)} });