Files
org_web/tests/princess-lima-polish.test.cjs
gitea-actions 563d4b63cc
All checks were successful
Build Org Website / build (push) Successful in 37s
Tune Princess Lima v2 boss balance and Sun Crystal aid
2026-07-30 15:36:59 +01:00

109 lines
5.0 KiB
JavaScript
Executable File

const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const data = require("../assets/scripts/pages/princess-lima-v2-data.js");
const state = require("../assets/scripts/pages/princess-lima-v2-state.js");
const systems = require("../assets/scripts/pages/princess-lima-v2-systems.js");
const root = path.resolve(__dirname, "..");
const source = (file) => fs.readFileSync(path.join(root, file), "utf8");
test("v2 content contracts and all dialogue graphs validate", () => {
assert.deepEqual(data.validateAll(), []);
assert.equal(data.SCHEMA_VERSION, 2);
assert.equal(Object.keys(data.DIALOGUES).length >= 8, true);
Object.values(data.DIALOGUES).forEach((dialogue) => assert.equal(data.validateDialogue(dialogue), true));
});
test("all nine regions are genuine Tiled maps with authored runtime layers", () => {
assert.equal(data.MAP_IDS.length, 9);
data.MAP_IDS.forEach((region) => {
const map = JSON.parse(source(`assets/maps/princess-lima/${region}.json`));
assert.equal(map.type, "map");
assert.equal(map.tilewidth, 32);
assert.equal(map.tileheight, 32);
assert.deepEqual(systems.validateMap(map, data.MAP_IDS), [], region);
assert.deepEqual(map.layers.map((layer) => layer.name), systems.REQUIRED_MAP_LAYERS);
assert.ok(map.layers.find((layer) => layer.name === "Base terrain").data.some((tile) => tile > 0));
});
});
test("the runtime loads Tiled maps rather than regional backdrop frames", () => {
const maps = source("assets/scripts/pages/princess-lima-v2-maps.js");
const scenes = source("assets/scripts/pages/princess-lima-v2-scenes.js");
assert.match(maps, /make\.tilemap/);
assert.match(maps, /createLayer/);
assert.match(scenes, /tilemapTiledJSON/);
assert.doesNotMatch(maps, /regional-style-atlas|REGION_FRAME/);
});
test("character animation, cinematic dialogue and eight enemy roles are present", () => {
const roles = new Set(Object.values(data.ENEMIES).filter((enemy) => !enemy.boss).map((enemy) => enemy.role));
["melee", "shield", "ranged", "fast", "ambush", "caster", "flying", "support", "elite"].forEach((role) => assert.ok(roles.has(role)));
assert.ok(fs.existsSync(path.join(root, "assets/images/play/princess-lima/actor-atlas-v2.png")));
assert.ok(fs.existsSync(path.join(root, "assets/images/play/princess-lima/portrait-atlas-v2.png")));
const ui = source("assets/scripts/pages/princess-lima-v2-ui.js");
assert.match(ui, /lima-cinematic-dialogue/);
assert.match(ui, /aria-label/);
assert.match(ui, /Conversation history/);
});
test("the standalone page loads only the local v2 runtime and accessibility surface", () => {
const rpg = source("play/rpg.org");
assert.match(rpg, /easystar-0\.4\.4\.min\.js/);
assert.match(rpg, /princess-lima-v2-game\.js/);
assert.match(rpg, /data-lima-screenreader/);
assert.match(rpg, /Block Shift/);
assert.doesNotMatch(rpg, /princess-lima-game\.js\?v=/);
assert.doesNotMatch(rpg, /https?:\/\//);
});
test("v1 progress migrates to v2 without overwriting legacy fields", () => {
const legacy = {
version: 1,
player: { name: "Rowan", appearance: "pine" },
chapter: 4,
region: "fortressInterior",
quests: { break_wards: { status: "complete", count: 3, rewarded: true } },
inventory: [{ id: "tempered_sword", quantity: 1 }, { id: "bad_item", quantity: 5 }],
defeatedBosses: ["stone_guardian", "unknown"],
settings: { subtitles: false, highContrast: true },
introSeen: true
};
const migrated = state.migrateLegacy(legacy);
assert.equal(migrated.version, 2);
assert.equal(migrated.player.name, "Rowan");
assert.equal(migrated.quests.break_wards.status, "complete");
assert.equal(migrated.inventory.some((item) => item.id === "bad_item"), false);
assert.equal(migrated.position.spawn, "frontHall");
assert.equal(migrated.settings.highContrast, true);
});
test("combat phases, hitboxes and enemy states are deterministic", () => {
assert.equal(systems.attackPhase(0, "charged"), "windup");
assert.equal(systems.attackPhase(520, "charged"), "active");
assert.equal(systems.attackPhase(670, "charged"), "recovery");
const north = systems.attackHitbox("north", 100, 100, "light1");
assert.ok(north.y + north.height <= 100);
const enemy = data.ENEMIES.raider;
assert.equal(systems.nextEnemyState("patrol", {
distance: 30, homeDistance: 0, spec: enemy, health: 5, leash: 190,
stunned: false, telegraphDone: true, attackDone: true, cooldownDone: true
}), "telegraph");
});
test("Malrec is demanding but leaves readable counterplay", () => {
const malrec = data.ENEMIES.malrec;
assert.equal(malrec.phases, 3);
assert.ok(malrec.health <= 52);
assert.ok(malrec.damage <= 13);
assert.ok(malrec.telegraph >= 750);
assert.ok(malrec.cooldown >= 1500);
const scenes = source("assets/scripts/pages/princess-lima-v2-scenes.js");
assert.match(scenes, /phase \+ 1/);
assert.match(scenes, /state\.health \+ 25/);
assert.match(scenes, /defences_disabled/);
assert.match(scenes, /prisoners_freed/);
});