← back to Allnewsdaily
scripts/short/formats.test.js
61 lines
"use strict";
const test = require('node:test');
const assert = require('node:assert/strict');
const { buildScript } = require('./build-script');
const { editionFormat, validateScript } = require('./formats');
const fixture = date => ({ date, stories: [
{ headline: 'Fixture: The museum opened a new exhibition on Monday.', outlet: 'Fixture Civic Desk' },
{ headline: 'Fixture: The library added evening hours for visitors.', outlet: 'Fixture Library Record' },
{ headline: 'Fixture: The city published its annual transport report.', outlet: 'Fixture City Bulletin' },
{ headline: 'Fixture: The university announced three public science lectures.', outlet: 'Fixture Campus Desk' },
{ headline: 'Fixture: The park reopened its walking path on Tuesday.', outlet: 'Fixture Parks Record' },
] });
test('same edition is identical and consecutive editions cover three formats', () => {
const ids = new Set(); const intros = new Set(); const outros = new Set();
for (const date of ['2026-09-10', '2026-09-11', '2026-09-12']) {
const data = fixture(date); const a = buildScript(data);
assert.deepEqual(a, buildScript(data)); ids.add(a.format); intros.add(a.intro.text); outros.add(a.outro.text);
assert.deepEqual(a.beats.map(b => [b.headline, b.outlet]), data.stories.map(b => [b.headline, b.outlet]));
assert.ok(a.totalSec <= 58); validateScript(a);
}
assert.equal(ids.size, 3); assert.equal(intros.size, 3); assert.equal(outros.size, 3);
});
test('dates and missing attribution fail closed', () => {
for (const d of ['', 'bad', '2026-02-30', '2026-13-01']) assert.throws(() => editionFormat(d));
assert.throws(() => buildScript({date:'2026-09-10',stories:[]}));
assert.throws(() => buildScript({date:'2026-09-10',stories:[{headline:'Fixture'}]}));
});
test('overlong facts are rejected without rewriting them', () => {
const data = fixture('2026-09-10'); data.stories = data.stories.slice(0,3).map(s => ({...s, headline: 'Fact '.repeat(160)}));
assert.throws(() => buildScript(data), /58 seconds/);
});
test('renderer input rejects tampered duration, format, beat order and zero timing', () => {
for (const mutate of [s => s.totalSec = 59, s => s.totalSec = 1, s => s.format = 'unknown', s => s.beats[0].n = 4, s => s.intro.estSec = 0]) {
const s = buildScript(fixture('2026-09-10')); mutate(s); assert.throws(() => validateScript(s));
}
});
module.exports = { fixture };
test('real ImageMagick fitting preserves short size and shrinks long accepted headlines', () => {
const fs=require('fs'),os=require('os'),path=require('path');
const { execFileSync }=require('child_process');
const { fitCaption }=require('./fit-caption');
const { FORMATS }=require('./formats');
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'short-fit-proof-'));
const file=path.join(dir,'headline.txt');
const font='/System/Library/Fonts/Supplemental/Arial Bold.ttf';
const magick='/opt/homebrew/bin/magick';
const long='Fixture: '+Array(68).fill('museum').join(' ')+'.';
const data=fixture('2026-09-10'); data.stories=data.stories.slice(0,3); data.stories[0].headline=long;
assert.ok(buildScript(data).totalSec<=58);
fs.writeFileSync(file,long);
for(const layout of FORMATS) {
const size=fitCaption(magick,font,file,layout.box); assert.ok(size<100);
const [w,h]=layout.box.split('x').map(Number);
const measured=Number(execFileSync(magick,['-font',font,'-pointsize',String(size),'-size',`${w}x`,`caption:@${file}`,'-format','%h','info:'],{encoding:'utf8'}));
assert.ok(measured<=h);
}
fs.writeFileSync(file,fixture('2026-09-10').stories[0].headline);
for(const layout of FORMATS) assert.equal(fitCaption(magick,font,file,layout.box),100);
});