/** * No movement hall floor tint may blow out as near-white. * node --test server/__tests__/floor-brightness.test.js */ const { describe, it } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const ROOT = path.join(__dirname, '..', '..'); const STYLES = fs.readFileSync(path.join(ROOT, 'client/src/data/movement-interior-styles.ts'), 'utf8'); /** Soft ceiling — mid stone / warm wood, not gallery flood. */ const MAX_FLOOR_LUMINANCE = 0.58; function luminance(hex) { const n = parseInt(hex.slice(1), 16); const r = (n >> 16) & 255; const g = (n >> 8) & 255; const b = n & 255; return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255; } function floorTints() { // tints objects inside mk(): wall/ceiling/floor hex triples (optional wallSide) const re = /\{\s*wall:\s*'#[^']+',(?:\s*wallSide:\s*'#[^']+',)?\s*ceiling:\s*'#[^']+',\s*floor:\s*'(#[0-9a-fA-F]{6})'/g; const out = []; let m; while ((m = re.exec(STYLES))) out.push(m[1]); return out; } describe('Movement floors — no blown-out brightness', () => { it('every authored floor tint stays at or below mid-stone luminance', () => { const floors = floorTints(); assert.ok(floors.length >= 20, `expected many floor tints, got ${floors.length}`); const offenders = floors .map((hex) => ({ hex, L: luminance(hex) })) .filter((f) => f.L > MAX_FLOOR_LUMINANCE); assert.deepEqual( offenders, [], offenders.map((f) => `${f.hex} L=${f.L.toFixed(3)}`).join(', ') ); }); it('Baroque floor is subdued warm marble, not near-white', () => { const start = STYLES.indexOf("'baroque-palace'"); assert.ok(start >= 0); const block = STYLES.slice(start, STYLES.indexOf('mk(', start + 1)); const m = block.match(/ceiling:\s*'#[^']+',\s*floor:\s*'(#[0-9a-fA-F]{6})'/); assert.ok(m); assert.ok(luminance(m[1]) <= MAX_FLOOR_LUMINANCE); assert.doesNotMatch(block, /floor: '#ece8e0'/); }); });