diff --git a/cocoscreator_projects/scripts/test/ui-source.test.mjs b/cocoscreator_projects/scripts/test/ui-source.test.mjs index 6e1609c..4dd140c 100644 --- a/cocoscreator_projects/scripts/test/ui-source.test.mjs +++ b/cocoscreator_projects/scripts/test/ui-source.test.mjs @@ -1,7 +1,7 @@ import { test } from 'node:test'; import assert from 'node:assert/strict'; import { join } from 'node:path'; -import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'; +import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { ROOT } from '../lib/paths.mjs'; import { loadSource, SourceError } from '../ui-migration/gameabc-source.mjs'; @@ -67,6 +67,50 @@ test('loadSource 对缺失文件显式抛错', () => { } finally { rmSync(dir, { recursive: true, force: true }); } }); +test('loadSource 缺少顶层数组键 ObjectList 时显式抛错', () => { + const dir = fakeSource({ + 'gameabc_Object.json': { Property: [] }, // 缺 ObjectList + }); + try { + assert.throws(() => loadSource(dir), SourceError); + assert.throws(() => loadSource(dir), /ObjectList/); + assert.throws(() => loadSource(dir), /gameabc_Object\.json/); + } finally { rmSync(dir, { recursive: true, force: true }); } +}); + +test('loadSource 缺少顶层数组键 ImageFileList 时显式抛错', () => { + const dir = fakeSource({ + 'gameabc_Image.json': { id: 1 }, // 缺 ImageFileList + }); + try { + assert.throws(() => loadSource(dir), SourceError); + assert.throws(() => loadSource(dir), /ImageFileList/); + assert.throws(() => loadSource(dir), /gameabc_Image\.json/); + } finally { rmSync(dir, { recursive: true, force: true }); } +}); + +test('loadSource 缺少顶层数组键 LayerList 时显式抛错', () => { + const dir = fakeSource({ + 'gameabc_Layer.json': { Property: {} }, // 缺 LayerList + }); + try { + assert.throws(() => loadSource(dir), SourceError); + assert.throws(() => loadSource(dir), /LayerList/); + assert.throws(() => loadSource(dir), /gameabc_Layer\.json/); + } finally { rmSync(dir, { recursive: true, force: true }); } +}); + +test('loadSource 缺少顶层数组键 GroupList 时显式抛错', () => { + const dir = fakeSource({ + 'gameabc_GroupList.json': { data: [] }, // 缺 GroupList + }); + try { + assert.throws(() => loadSource(dir), SourceError); + assert.throws(() => loadSource(dir), /GroupList/); + assert.throws(() => loadSource(dir), /gameabc_GroupList\.json/); + } finally { rmSync(dir, { recursive: true, force: true }); } +}); + test('loadSource 在真实源数据上得到已知规模(回归护栏)', () => { const s = loadSource(REAL_SOURCE); assert.equal(s.objects.size, 991, '对象总数'); diff --git a/cocoscreator_projects/scripts/ui-migration/gameabc-source.mjs b/cocoscreator_projects/scripts/ui-migration/gameabc-source.mjs index b64d967..324c20d 100644 --- a/cocoscreator_projects/scripts/ui-migration/gameabc-source.mjs +++ b/cocoscreator_projects/scripts/ui-migration/gameabc-source.mjs @@ -16,20 +16,31 @@ function readJson(dir, name) { } } +function readArray(dir, fileName, key) { + const json = readJson(dir, fileName); + const arr = json[key]; + if (!Array.isArray(arr)) { + throw new SourceError( + `源数据 ${fileName} 缺少顶层数组 "${key}"(实际为 ${typeof arr})`, + ); + } + return arr; +} + /** * 读取并校验 gameabc 导出数据。 * 源数组用 `{}` 占位空槽(下标即 ID),须跳过。 */ export function loadSource(outputDir) { const objects = new Map(); - for (const entry of readJson(outputDir, 'gameabc_Object.json').ObjectList ?? []) { + for (const entry of readArray(outputDir, 'gameabc_Object.json', 'ObjectList')) { if (!entry || !entry.Property) continue; const p = entry.Property; objects.set(p.ObjectID, { ...p, events: Object.keys(entry.Event ?? {}) }); } const images = new Map(); - for (const i of readJson(outputDir, 'gameabc_Image.json').ImageFileList ?? []) { + for (const i of readArray(outputDir, 'gameabc_Image.json', 'ImageFileList')) { if (!i || i.id === undefined) continue; if (i.w * i.h !== i.frame_all) { throw new SourceError( @@ -39,7 +50,7 @@ export function loadSource(outputDir) { images.set(i.id, i); } - const layers = (readJson(outputDir, 'gameabc_Layer.json').LayerList ?? []) + const layers = readArray(outputDir, 'gameabc_Layer.json', 'LayerList') .filter((l) => l && l.Property) .map((l) => ({ layerId: l.Property.LayerID, @@ -47,7 +58,7 @@ export function loadSource(outputDir) { objectIds: l.ObjectList ?? [], })); - const groups = (readJson(outputDir, 'gameabc_GroupList.json').GroupList ?? []) + const groups = readArray(outputDir, 'gameabc_GroupList.json', 'GroupList') .filter((g) => g && g.GroupID !== undefined) .map((g) => ({ groupId: g.GroupID, objectIds: g.ObjectList ?? [] }));