import { describe, test } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { execFileSync } from 'node:child_process';
import {
TARGETS,
DEFAULT_TOKENS,
deriveDimensions,
loadBrandTokens,
tokensToCss,
wrapSvg,
injectTokens,
parseSvgMeta,
validateFigure,
resolveChrome,
parseArgs,
renderFigure,
} from '../build-figur.mjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const FIXTURE_SVG = path.join(__dirname, 'fixtures', 'demo-figur.svg');
function tmpdir() {
return fs.mkdtempSync(path.join(os.tmpdir(), 'build-figur-test-'));
}
// Leser bredde/høyde fra PNG-IHDR (byte 16–23, big-endian).
function pngDimensions(file) {
const buf = fs.readFileSync(file);
return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
}
describe('TARGETS + deriveDimensions', () => {
test('carousel er 1080×1350 (fast)', () => {
assert.equal(TARGETS.carousel.width, 1080);
assert.equal(TARGETS.carousel.height, 1350);
});
test('article er 1200 bred med innholdsstyrt høyde', () => {
assert.equal(TARGETS.article.width, 1200);
assert.equal(TARGETS.article.height, null);
});
test('single er kvadratisk 1200×1200', () => {
assert.equal(TARGETS.single.width, 1200);
assert.equal(TARGETS.single.height, 1200);
});
test('article + aspekt 16:9 gir avledet høyde 675', () => {
const d = deriveDimensions('article', 1200 / 675, {});
assert.equal(d.width, 1200);
assert.equal(d.height, 675);
assert.equal(d.warnings.length, 0);
});
test('article uten aspekt gir fallback-høyde med warning', () => {
const d = deriveDimensions('article', null, {});
assert.equal(d.width, 1200);
assert.ok(d.height > 0);
assert.ok(d.warnings.some((w) => /høyde/i.test(w)));
});
test('--width/--height-overstyringer vinner over målet', () => {
const d = deriveDimensions('carousel', null, { width: 800, height: 600 });
assert.equal(d.width, 800);
assert.equal(d.height, 600);
});
test('ukjent mål kaster med gyldige mål i meldingen', () => {
assert.throws(() => deriveDimensions('poster', null, {}), /article/);
});
});
describe('loadBrandTokens — token-seam (brukerdata, aldri hardkodet)', () => {
test('uten brand-tokens.json: nøytrale defaults, source=default', () => {
const dir = tmpdir();
const r = loadBrandTokens(dir);
assert.equal(r.source, 'default');
assert.deepEqual(r.tokens, DEFAULT_TOKENS);
assert.equal(r.warnings.length, 0);
});
test('gyldig fil merges over defaults, source=user', () => {
const dir = tmpdir();
fs.mkdirSync(path.join(dir, 'profile'), { recursive: true });
fs.writeFileSync(
path.join(dir, 'profile', 'brand-tokens.json'),
JSON.stringify({ accent: '#123456' }),
'utf8'
);
const r = loadBrandTokens(dir);
assert.equal(r.source, 'user');
assert.equal(r.tokens.accent, '#123456');
// umerkede felter beholder defaults
assert.equal(r.tokens.background, DEFAULT_TOKENS.background);
});
test('ugyldig JSON: defaults + warning, kaster aldri', () => {
const dir = tmpdir();
fs.mkdirSync(path.join(dir, 'profile'), { recursive: true });
fs.writeFileSync(path.join(dir, 'profile', 'brand-tokens.json'), '{not json', 'utf8');
let r;
assert.doesNotThrow(() => { r = loadBrandTokens(dir); });
assert.equal(r.source, 'default');
assert.ok(r.warnings.some((w) => /brand-tokens\.json/.test(w)));
});
});
describe('tokensToCss + wrapSvg + injectTokens — CSS-injeksjon', () => {
test('tokensToCss produserer --figur-variabler for alle tokens', () => {
const css = tokensToCss(DEFAULT_TOKENS);
assert.match(css, /:root\{/);
for (const key of Object.keys(DEFAULT_TOKENS)) {
assert.ok(css.includes(`--figur-${key}`), `mangler --figur-${key}`);
}
});
test('egendefinerte tokenverdier reflekteres i CSS-en', () => {
const css = tokensToCss({ ...DEFAULT_TOKENS, accent: '#ABCDEF' });
assert.match(css, /--figur-accent:\s*#ABCDEF/);
});
test('wrapSvg bygger komplett HTML med svg + tokens + mål-dimensjoner', () => {
const html = wrapSvg('', { width: 1080, height: 1350 }, DEFAULT_TOKENS);
assert.match(html, //i);
assert.match(html, /