Files
pgames-manager/frontend/tests/gallery-unlock.test.mjs
T
mrleo1nid 19924e1c5a
CI / frontend (push) Failing after 22s
CI / backend (push) Failing after 42s
feat: unlock in-game galleries per owned version
2026-10-27 15:45:22 +03:00

112 lines
5.8 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { test } from "node:test";
import { build } from "esbuild";
import { makeEngine, until } from "./helpers/renpy-engine.mjs";
const built = await build({ stdin: { contents: `export {parseStatements} from './pyparse'; export {galleryScreens} from './gallery-access';`,
resolveDir: fileURLToPath(new URL("../src/player/", import.meta.url)), loader: "ts" },
bundle: true, write: false, format: "esm", platform: "node", logLevel: "silent" });
const { parseStatements, galleryScreens } = await import(`data:text/javascript;base64,${Buffer.from(built.outputFiles[0].text).toString("base64")}`);
const runtime = () => ({ game: { resolution: [640, 480], entry: "start", ui: {}, files: {},
defaults: [], init: parseStatements('g = Gallery()\ng.button("Art")\ng.image("picture")\ng.condition("False")'),
characters: {}, channels: {}, images: { picture: { color: "#fff" } }, transforms: {}, atl: {}, transitions: {}, screens: {}, styles: {}, audio: {} },
modules: [{ labels: [{ name: "start", body: [{ op: "pause", seconds: null }] }], anchors: {} }] });
test("Gallery conditions stay locked normally and are bypassed only for the opted-in version", () => {
for (const enabled of [false, true]) {
const { engine } = makeEngine(runtime(), { galleryUnlocked: enabled });
try {
engine.newStore();
const action = engine.py.attrOf(engine.store.g, "Action")("Art");
assert.equal(action !== null, enabled);
engine.openUnlockedGallery();
assert.equal(engine.gallerySession, enabled);
} finally { engine.destroy(); }
}
});
test("gallery browsing does not write saves or persistent progress and restores the running story", async () => {
const { engine, root } = makeEngine(runtime(), { galleryUnlocked: true });
let saves = 0;
engine.saves.put = async () => { saves++; };
try {
engine.newStore();
engine.persistent.example = ["story"];
engine.store.counter = 7;
engine.history = [{ name: "Player", text: "Story history" }];
engine.seenImages.add("story");
engine.cur = { label: "start", path: [0] };
engine.running = true;
engine.openUnlockedGallery();
assert.match(root.textContent, /Art/);
engine.persistent.example.push("gallery");
engine.persistent.other = true;
engine.seenImages.add("gallery");
await engine.save("auto");
assert.equal(saves, 0);
engine.closeUnlockedGallery();
assert.deepEqual(engine.persistent.example, ["story"]);
assert.equal(engine.persistent.other, null);
assert.equal(engine.store.counter, 7);
assert.deepEqual(engine.history, [{ name: "Player", text: "Story history" }]);
assert.deepEqual([...engine.seenImages], ["story"]);
assert.equal(engine.gallerySession, false);
} finally { engine.destroy(); }
});
test("MIST unlock modifies only gallery expressions and keeps page and story conditions intact", () => {
const condition = { k: "attr", o: { k: "name", id: "scene" }, a: "watched" };
const page = { k: "cmp", l: { k: "name", id: "curpage" }, ops: ["=="], r: [{ k: "const", v: "Page 1" }] };
const game = { build: { name: "MIST" }, screens: { rewatchgirlselectscreen: {},
robgallery: { condition, page }, story: { condition } } };
const unlocked = galleryScreens(game, true);
assert.deepEqual(unlocked.robgallery.condition, { k: "const", v: true });
assert.deepEqual(unlocked.robgallery.page, page);
assert.equal(unlocked.story, game.screens.story);
assert.deepEqual(game.screens.robgallery.condition, condition);
assert.equal(galleryScreens(game, false), game.screens);
});
test("MIST's actual gallery opens all character and scene buttons without completing quests", {
skip: !process.env.PGAMES_MIST_RUNTIME,
}, async () => {
const original = JSON.parse(readFileSync(process.env.PGAMES_MIST_RUNTIME, "utf8"));
const { engine, root } = makeEngine(original, { galleryUnlocked: true });
engine.fileUrl = (path) => `/mock/${path}`;
engine.audio.play = () => {};
try {
engine.openUnlockedGallery();
await until(() => root.querySelectorAll(".pg-simgbtn").length >= 5);
assert.equal(engine.py.attrOf(engine.store.searchlodge07, "is_done"), false);
const robin = root.querySelectorAll(".pg-simgbtn")[1];
robin.onclick({ stopPropagation() {} });
await until(() => engine.screens.shown.has("robgallery"));
const images = [...engine.screens.shown.get("robgallery").el.querySelectorAll("img")].map((image) => image.src);
assert.ok(images.some((path) => path.includes("g_r01")));
assert.equal(images.some((path) => path.includes("gallocked")), false);
assert.equal(engine.py.attrOf(engine.store.rscene01, "watched"), false);
for (const [name, pages] of [["robgallery", 3], ["miagallery", 2], ["lilgallery", 2], ["norgallery", 2], ["groupgallery", 1]]) {
engine.screens.show(name);
const instance = engine.screens.shown.get(name);
for (let page = 1; page <= pages; page++) {
instance.vars.set("curpage", `Page ${page}`);
engine.screens.render(instance);
const pictures = [...instance.el.querySelectorAll("img")].map((image) => image.src);
assert.equal(pictures.some((path) => path.includes("gallocked")), false, `${name}, page ${page}`);
}
}
engine.store.replaywatch = true;
engine.jump("myroomcontrol");
assert.equal(engine.cur.label, "rewatchscene");
engine.screens.show("rewatchgirlselectscreen");
engine.store.replaywatch = false;
engine.screens.shown.get("rewatchgirlselectscreen").el.querySelector(".pg-simgbtn").onclick({ stopPropagation() {} });
await new Promise((resolve) => setTimeout(resolve, 20));
assert.equal(engine.gallerySession, false);
assert.equal(engine.running, false);
assert.equal(engine.cur.label, "");
} finally { engine.destroy(); }
});