'use strict'; const assert = require('assert/strict'); const fs = require('fs'); const os = require('os'); const path = require('path'); const { normalizeItem } = require('../js/eagle-compound'); const generate = require('../thumbnail/spine'); const { canvasLib } = require('../js/spine-render'); const root = fs.mkdtempSync(path.join(os.tmpdir(), 'spine-compound-thumbnail-')); const fixtures = path.resolve(process.argv[2] || path.join(__dirname, '34-43/3.4/spineboy')); (async () => { try { for (const [name, ext, full] of [['lili.atlas', 'txt', 'atlas.txt'], ['lili.skel', 'bytes', 'skel.bytes']]) { const item = { name, ext, text: 'old text' }; const source = path.join(root, name + '.' + ext); assert(normalizeItem(source, item)); assert.equal(item.name, 'lili'); assert.equal(item.ext, full); assert.equal(path.basename(source), item.name + '.' + item.ext); assert.equal(item.text, undefined); assert.equal(normalizeItem(source, item), false); } for (const text of ['', 'ordinary text\nsecond line']) { const src = path.join(root, 'notes.txt'); fs.writeFileSync(src, text); const result = await generate({ src, dest: path.join(root, 'notes_thumbnail.png'), item: { name: 'notes', ext: 'txt' } }); assert.equal(result.text, text); assert.equal(result.ext, 'txt'); assert.equal(result.name, 'notes'); } const ordinaryJson = path.join(root, 'config.json'); const ordinaryJsonDest = path.join(root, 'config_thumbnail.png'); fs.writeFileSync(ordinaryJson, JSON.stringify({ skeleton: { spine: null }, enabled: true })); const ordinaryJsonItem = { name: 'config', ext: 'json' }; assert.equal(await generate({ src: ordinaryJson, dest: ordinaryJsonDest, item: ordinaryJsonItem }), ordinaryJsonItem); assert.equal(fs.existsSync(ordinaryJsonDest), false, 'non-Spine JSON must leave thumbnail destination untouched'); assert.equal(ordinaryJsonItem.width, undefined); assert.equal(ordinaryJsonItem.height, undefined); for (const [source, target] of [['spineboy-mesh.skel', 'spineboy.skel.bytes'], ['spineboy.atlas', 'spineboy.atlas.txt'], ['spineboy.png', 'spineboy.png']]) fs.copyFileSync(path.join(fixtures, source), path.join(root, target)); for (const [name, ext] of [['spineboy.atlas', 'txt'], ['spineboy.skel', 'bytes']]) { const src = path.join(root, name + '.' + ext), before = fs.readFileSync(src); const dest = path.join(root, name + '_thumbnail.png'); const result = await generate({ src, dest, item: { name, ext } }); assert.equal(result.name, 'spineboy'); assert.equal(result.ext, name.split('.')[1] + '.' + ext); assert(before.equals(fs.readFileSync(path.join(root, result.name + '.' + result.ext)))); const canonical = path.join(root, 'spineboy_thumbnail.png'); assert(fs.existsSync(dest)); assert(fs.existsSync(canonical)); assert.equal(fs.readFileSync(canonical).subarray(8,12).toString(), 'WEBP'); const image = await canvasLib.loadImage(canonical), canvas = canvasLib.createCanvas(400,400); const ctx = canvas.getContext('2d'); ctx.drawImage(image,0,0); const pixels = ctx.getImageData(12,356,110,32).data; let white = 0; for (let i = 0; i < pixels.length; i += 4) if (pixels[i]>190 && pixels[i+1]>190 && pixels[i+2]>190) white++; assert(white > 50, 'bottom-left version watermark must be visible'); } console.log('PASS: ordinary TXT remains visible, compound metadata round-trip, canonical WebP cache and version watermark'); } finally { fs.rmSync(root, { recursive: true, force: true }); } })().catch(error => { console.error(error); process.exitCode = 1; });