// tests/write.test.js const { test } = require('node:test'); const assert = require('node:assert'); const path = require('node:path'); const fs = require('node:fs'); const { PNG } = require('pngjs'); const { composeAtlas, buildAtlasJson } = require('../src/write'); const FIX = path.resolve(__dirname, 'fixtures/small-fresh'); test('compose: 3 tiles into 96x32 atlas', async () => { const placed = [ { name: 'grass', id: 1, x: 0, y: 0, w: 32, h: 32, diffusePath: path.join(FIX, 'grass_diffuse.png'), heightPath: null }, { name: 'stone', id: 2, x: 32, y: 0, w: 32, h: 32, diffusePath: path.join(FIX, 'stone_diffuse.png'), heightPath: null }, { name: 'water', id: 3, x: 64, y: 0, w: 32, h: 32, diffusePath: path.join(FIX, 'water_diffuse.png'), heightPath: path.join(FIX, 'water_height.png') }, ]; const { diffuse, height } = await composeAtlas(placed, 128, 32); assert.strictEqual(diffuse.width, 128); assert.strictEqual(diffuse.height, 32); // Sample grass pixel (0,0): expect RGB ~ (110, 170, 80) const gIdx = 0; assert.strictEqual(diffuse.data[gIdx + 0], 110); assert.strictEqual(diffuse.data[gIdx + 1], 170); assert.strictEqual(diffuse.data[gIdx + 2], 80); // Sample stone pixel (32, 0): expect RGB ~ (120, 120, 120) const sIdx = (0 * 128 + 32) * 4; assert.strictEqual(diffuse.data[sIdx + 0], 120); // Height should be 0 for synth-tiles (grass, stone) assert.strictEqual(height.data[0], 0); // grass position assert.strictEqual(height.data[(0 * 128 + 32) * 4], 0); // stone position }); test('buildAtlasJson: sorted by ID, blocks_sight pattern applied', () => { const placed = [ { name: 'grass', id: 1, x: 0, y: 0, w: 32, h: 32 }, { name: 'stone_wall_brick', id: 2, x: 32, y: 0, w: 32, h: 32 }, ]; const json = buildAtlasJson('demo', 1, 32, 64, 32, placed, /^stone_wall_/); assert.strictEqual(json.atlas_id, 'demo'); assert.strictEqual(json.tiles.length, 2); assert.strictEqual(json.tiles[0].id, 1); assert.strictEqual(json.tiles[0].name, 'grass'); assert.deepStrictEqual(json.tiles[0].uv, [0, 0, 32, 32]); assert.strictEqual(json.tiles[0].blocks_sight, undefined); assert.strictEqual(json.tiles[1].blocks_sight, true); }); test('compose: WebP source loads + resizes', async () => { // Optional integration check — only runs if FA-Starter fixture is staged. // The Task 10 baker run is the real-world verification; this test is a // cheaper inline guard against jimp.read regressions on WebP. const webpFixture = path.resolve(__dirname, 'fixtures/multi-format/sample_diffuse.webp'); if (!fs.existsSync(webpFixture)) return; // skip if not staged const placed = [{ name: 'sample', id: 1, x: 0, y: 0, w: 64, h: 64, diffusePath: webpFixture, heightPath: null }]; const { diffuse } = await composeAtlas(placed, 64, 64); assert.strictEqual(diffuse.width, 64); assert.strictEqual(diffuse.height, 64); // Just confirm it didn't throw and has nonzero alpha somewhere let hasAlpha = false; for (let i = 3; i < diffuse.data.length; i += 4) { if (diffuse.data[i] > 0) { hasAlpha = true; break; } } assert.ok(hasAlpha, 'compose produced fully-transparent atlas'); });