32 lines
2.9 KiB
JavaScript
32 lines
2.9 KiB
JavaScript
import {test} from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {readFileSync} from 'node:fs';
|
|
import {nativeLayout,defaultText,textSize,imageShape,designUrl,newElementId} from '../src/lib/native-layout.ts';
|
|
const layouts=JSON.parse(readFileSync(new URL('../src/content/templates/layouts.json',import.meta.url)));
|
|
test('all 27 native pages are independent of source images and supply actual text',()=>{
|
|
for(const original of layouts){const layout=nativeLayout(original),serialized=JSON.stringify(layout);assert.ok(!serialized.includes('data:image'));assert.ok(!serialized.includes('raster'));assert.ok(!serialized.includes('source'));assert.ok(serialized.length<7000);
|
|
for(const slot of layout.elements.filter(s=>s.kind==='text')){assert.ok(defaultText(layout,slot).length);for(const text of [defaultText(layout,slot),'','新的文字\n第二行','<script>plain text</script>']){const size=textSize(layout,slot,text);assert.ok(Number.isFinite(size)&&size>0)}}
|
|
}
|
|
});
|
|
test('geometric templates retain their distinct silhouettes without bitmap masks',()=>{
|
|
assert.equal(imageShape(layouts[4],layouts[4].elements.find(s=>s.id==='I03')).borderRadius,'50%');
|
|
assert.ok(imageShape(layouts[21],layouts[21].elements.find(s=>s.id==='I02')).clipPath.startsWith('polygon('));
|
|
assert.notEqual(imageShape(layouts[21],layouts[21].elements.find(s=>s.id==='I02')).clipPath,imageShape(layouts[26],layouts[26].elements.find(s=>s.id==='I02')).clipPath);
|
|
assert.equal(designUrl('ref-01','abc'),'/studio/template-layout?id=abc');
|
|
assert.equal(designUrl('standard-01','abc'),'/studio/template-layout?id=abc');
|
|
});
|
|
|
|
test('element IDs work on HTTP origins without secure-context randomUUID',()=>{const saved=Object.getOwnPropertyDescriptor(globalThis,'crypto');let calls=0;Object.defineProperty(globalThis,'crypto',{configurable:true,value:{getRandomValues(array){for(let i=0;i<array.length;i++)array[i]=++calls;return array}}});try{const first=newElementId(),second=newElementId();assert.match(first,/^user-[a-f0-9]{32}$/);assert.notEqual(first,second)}finally{Object.defineProperty(globalThis,'crypto',saved)}});
|
|
|
|
// Measured by ces-control Chromium/Noto in production run 660, at 860px art width.
|
|
// These glyph bounds exceed CSS line-height, including single-letter title slots.
|
|
test('default copy leaves room for production fallback glyph bounds',()=>{
|
|
const cases=[['ref-07','T02',97/73.7143],['ref-08','T01',112/85.2389],['ref-19','T01',287/217.055],['ref-19','T02',298/225.274],['ref-19','T03',313/237.451],['ref-19','T04',306/231.363],['ref-21','T01',195/160.307]];
|
|
for(const [id,slotId,glyphExtent] of cases){
|
|
const layout=layouts.find(l=>l.id===id),slot=layout.elements.find(s=>s.id===slotId);
|
|
const size=textSize(layout,slot,defaultText(layout,slot))*860/layout.width;
|
|
const crossAxis=slot.direction==='rotated'?slot.box[2]:slot.box[3];
|
|
assert.ok(Math.ceil(size*glyphExtent)<=crossAxis*860/layout.width+2,`${id}/${slotId}: fallback glyphs must fit`);
|
|
}
|
|
});
|