Initial REM planning editor
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
import { buildUniverseMap } from "./build-universe-map.mjs";
|
||||
|
||||
const html = readFileSync(new URL("./star-map.html", import.meta.url), "utf8");
|
||||
const startScript = readFileSync(new URL("./start-webserver.cmd", import.meta.url), "utf8");
|
||||
|
||||
function extractJsonScript(id) {
|
||||
const pattern = new RegExp(`<script type="application/json" id="${id}">([\\s\\S]*?)</script>`);
|
||||
const match = html.match(pattern);
|
||||
assert.ok(match, `missing JSON script #${id}`);
|
||||
return JSON.parse(match[1]);
|
||||
}
|
||||
|
||||
test("universe import builds a grid map from universe.ini", async () => {
|
||||
const data = await buildUniverseMap({
|
||||
universeDir: "C:/PROJECTS/REM/REM-Mod/DATA/UNIVERSE"
|
||||
});
|
||||
|
||||
assert.equal(data.source, "universe.ini");
|
||||
assert.equal(data.systems.length, 58);
|
||||
assert.ok(data.connections.length >= 80, "expected direct jump connections from system INIs");
|
||||
assert.deepEqual(data.grid, {
|
||||
minCol: 0,
|
||||
minRow: 0,
|
||||
maxCol: 16,
|
||||
maxRow: 16,
|
||||
cellSize: 72,
|
||||
margin: 72
|
||||
});
|
||||
|
||||
const newYork = data.systems.find((system) => system.id === "li01");
|
||||
assert.ok(newYork, "missing Li01");
|
||||
assert.equal(newYork.name, "New York");
|
||||
assert.equal(newYork.gridX, 7);
|
||||
assert.equal(newYork.gridY, 8);
|
||||
assert.equal(newYork.x, 576);
|
||||
assert.equal(newYork.y, 648);
|
||||
|
||||
assert.ok(data.connections.some((connection) => connection.from === "li01" && connection.to === "li02"));
|
||||
});
|
||||
|
||||
test("template contains the systems and connections from universe.ini", () => {
|
||||
const data = extractJsonScript("initial-map-data");
|
||||
|
||||
assert.equal(data.source, "universe.ini");
|
||||
assert.equal(data.systems.length, 58, "expected active systems from universe.ini");
|
||||
assert.ok(data.connections.length >= 80, "expected direct route set from system INIs");
|
||||
assert.ok(data.grid, "expected grid metadata");
|
||||
|
||||
for (const name of ["Tau-37", "Leeds", "New Tokyo", "Sigma-19", "New Berlin", "Omicron Prime"]) {
|
||||
assert.ok(data.systems.some((system) => system.name === name), `missing ${name}`);
|
||||
}
|
||||
|
||||
for (const connection of data.connections) {
|
||||
assert.ok(data.systems.some((system) => system.id === connection.from), `missing source ${connection.from}`);
|
||||
assert.ok(data.systems.some((system) => system.id === connection.to), `missing target ${connection.to}`);
|
||||
}
|
||||
});
|
||||
|
||||
test("editor UI exposes creation, deletion, linking, notes, and background controls", () => {
|
||||
assert.match(html, /data-mode="add"/, "missing add-system mode");
|
||||
assert.match(html, /data-mode="connect"/, "missing connection mode");
|
||||
assert.match(html, /id="deleteSelected"/, "missing delete action");
|
||||
assert.match(html, /id="systemNote"/, "missing per-system note field");
|
||||
assert.match(html, /id="backgroundColor"/, "missing configurable background");
|
||||
assert.match(html, /id="saveRemote"/, "missing persistent save action");
|
||||
assert.match(html, /id="loadRemote"/, "missing persistent load action");
|
||||
assert.doesNotMatch(html, /id="snapToGrid"/, "grid snapping must not be optional");
|
||||
assert.match(html, /function snapPointToGrid/, "missing grid snapping behavior");
|
||||
assert.doesNotMatch(html, /snapToGrid\.checked/, "grid snapping must always be active");
|
||||
assert.match(html, /function createSystem/, "missing system creation behavior");
|
||||
assert.match(html, /function createConnection/, "missing connection creation behavior");
|
||||
assert.match(html, /async function saveRemoteMap/, "missing server save behavior");
|
||||
assert.match(html, /async function loadRemoteMap/, "missing server load behavior");
|
||||
});
|
||||
|
||||
test("system dragging has a forgiving hit target and stable pointer handlers", () => {
|
||||
assert.match(html, /class", "system-hit"/, "missing large system drag hit target");
|
||||
assert.match(html, /function handleDragMove/, "missing shared drag move handler");
|
||||
assert.match(html, /window\.addEventListener\("pointermove", handleDragMove\)/, "missing window pointermove handler");
|
||||
assert.match(html, /window\.addEventListener\("pointerup", handleDragEnd\)/, "missing window pointerup handler");
|
||||
});
|
||||
|
||||
test("start script launches the local server and opens the browser", () => {
|
||||
assert.match(startScript, /node.*server\.mjs/i, "start script must launch server.mjs with node");
|
||||
assert.match(startScript, /127\.0\.0\.1:3000/i, "start script must open the local app URL");
|
||||
assert.match(startScript, /Start-Process/i, "start script must open the browser");
|
||||
});
|
||||
Reference in New Issue
Block a user