import assert from "node:assert/strict"; import { EventEmitter } from "node:events"; import path from "node:path"; import test from "node:test"; import { defaultWowExportExecutable, resolveWowExportExecutable, runWowExporter, WOW_EXPORT_REQUIRED_FLAGS, } from "./wow-export-tool.mjs"; const projectRoot = path.resolve("fixture/HealerMan"); const fileInfo = { isFile: () => true }; test("resolveWowExportExecutable uses the versioned portable path by default", async () => { const expected = defaultWowExportExecutable(projectRoot); const inspected = []; const resolved = await resolveWowExportExecutable({ projectRoot, environment: {}, statFile: async (file) => { inspected.push(file); return fileInfo; }, }); assert.equal(resolved, expected); assert.deepEqual(inspected, [expected]); }); test("resolveWowExportExecutable honors an explicit relative WOW_EXPORT_BIN", async () => { const expected = path.resolve(projectRoot, "tools/patched/wow.export.exe"); const resolved = await resolveWowExportExecutable({ projectRoot, environment: { WOW_EXPORT_BIN: "tools/patched/wow.export.exe" }, statFile: async (file) => { assert.equal(file, expected); return fileInfo; }, }); assert.equal(resolved, expected); }); test("resolveWowExportExecutable rejects a directory with actionable diagnostics", async () => { const configured = path.resolve(projectRoot, "tools/not-an-executable"); await assert.rejects( resolveWowExportExecutable({ projectRoot, environment: { WOW_EXPORT_BIN: configured }, statFile: async () => ({ isFile: () => false }), }), (error) => { assert.equal(error.code, "WOW_EXPORT_AUTOMATION_UNAVAILABLE"); assert.match(error.message, /path exists but is not a file/); assert.match(error.message, /Correct or unset that variable/); assert.match(error.message, /No export was started and no placeholder assets were created/); for (const flag of WOW_EXPORT_REQUIRED_FLAGS) assert.ok(error.message.includes(flag)); return true; }, ); }); test("resolveWowExportExecutable reports the exact missing default path", async () => { const expected = defaultWowExportExecutable(projectRoot); await assert.rejects( resolveWowExportExecutable({ projectRoot, environment: {}, statFile: async () => { const error = new Error("missing"); error.code = "ENOENT"; throw error; }, }), (error) => { assert.ok(error.message.includes(expected)); assert.match(error.message, /set WOW_EXPORT_BIN/); assert.match(error.message, /stock GUI-only wow\.export executable is not a drop-in replacement/); return true; }, ); }); test("runWowExporter supplies the non-updating automation invocation", async () => { const executable = path.resolve(projectRoot, "tools/wow.export.exe"); let invocation; const spawn = (file, args, options) => { invocation = { file, args, options }; const child = new EventEmitter(); queueMicrotask(() => child.emit("exit", 0, null)); return child; }; const result = await runWowExporter(["--export-dungeon", "--recipe", "fixture.json"], { projectRoot, environment: { WOW_EXPORT_BIN: executable }, statFile: async () => fileInfo, spawn, }); assert.equal(result, executable); assert.deepEqual(invocation.args, ["--disable-auto-update", "--export-dungeon", "--recipe", "fixture.json"]); assert.equal(invocation.options.cwd, path.dirname(executable)); assert.equal(invocation.options.windowsHide, true); }); test("runWowExporter identifies an incompatible executable after a failed launch", async () => { const executable = path.resolve(projectRoot, "tools/wow.export.exe"); const spawn = () => { const child = new EventEmitter(); queueMicrotask(() => child.emit("exit", 2, null)); return child; }; await assert.rejects( runWowExporter(["--export-creatures"], { projectRoot, environment: { WOW_EXPORT_BIN: executable }, statFile: async () => fileInfo, spawn, }), (error) => { assert.match(error.message, /process exited with code 2/); assert.match(error.message, /HealerMan-patched build/); assert.match(error.message, /No placeholder assets were created/); return true; }, ); });