feat: add native Tomie adapter and strict import compatibility checks

This commit is contained in:
mrleo1nid committed 2026-10-25 11:02:52 +03:00
1 parent c8d83b3fe7
commit 57a4edcbfd
45 files changed
+2985 -71

No files matched your search

+94
View File
@@ -0,0 +1,94 @@
// Optional browser integration: PGAMES_PLAYWRIGHT_PATH points at a Playwright
// installation; its matching Chromium must already be installed.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath, pathToFileURL } from "node:url";
import { test } from "node:test";
import { build } from "esbuild";
test("native Tomie UI plays, saves inside a call, restores and presents the board", {
skip: !process.env.PGAMES_PLAYWRIGHT_PATH,
}, async () => {
const { chromium } = await import(pathToFileURL(process.env.PGAMES_PLAYWRIGHT_PATH + "/index.mjs").href);
const entry = fileURLToPath(new URL("../src/player/tomie-player.ts", import.meta.url));
const bundle = await build({ entryPoints: [entry], bundle: true, write: false, format: "iife", globalName: "NativeTomie",
platform: "browser", loader: { ".css": "empty" }, logLevel: "silent" });
const browser = await chromium.launch({ headless: true, executablePath: process.env.PGAMES_CHROMIUM_EXECUTABLE });
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
const faults = [];
page.on("pageerror", (error) => faults.push(error.message));
try {
await page.goto("about:blank");
await page.setContent('<style>body{margin:0}#host{height:800px}</style><div id="host"></div>');
await page.addStyleTag({ content: readFileSync(new URL("../src/player/tomie.css", import.meta.url), "utf8") });
await page.addScriptTag({ content: bundle.outputFiles[0].text });
await page.evaluate(() => {
const c = (op, args = {}) => ({ op, args });
const script = (commands, labels = {}) => ({ commands, labels });
const content = { defaults: { day: 1, money: 100, iWillpower: 100, dActionPoint: 4 }, function_data: {},
initial_inventory: { coin: 1 }, start: { script: "StartGameSetting", label: "Start" }, resources: {}, board_routes: {},
media: { backgrounds: {}, characters: {}, textures: {}, videos: {}, audio: {}, fonts: {} },
scripts: {
StartGameSetting: script([c("PrintText", { Text: "Hello" }), c("Gosub", { Path: "Sub" }),
c("InputCustomVariable", { VariableName: "name", Summary: "Your name" }),
c("AddChoice", { ChoiceSummary: "Choose", OnSelected: { template: '@set answer="{name}"' } }), c("Stop"),
c("Spawn", { Path: "Rain" }), c("PrintText", { Text: { template: "Saved {answer}" } }), c("Goto", { Path: "AppScreen" })], { Start: 0 }),
Sub: script([c("PrintText", { Text: "Subroutine" }), c("Return")]),
AppScreen: script([c("Stop")]),
UseItem: script([c("RemoveItem", { ItemId: "coin" }), c("PrintText", { Text: "Used coin" }),
c("Spawn", { Path: "PregMiniGame" }), c("Stop")], { coin: 0 }),
Common2: script([c("PrintText", { Text: "Won" }), c("Goto", { Path: "AppScreen" }),
c("PrintText", { Text: "Lost" }), c("Goto", { Path: "AppScreen" })], { PregNO: 0, PregOK: 2 }),
} };
const slots = new Map();
const api = { list: async () => [...slots].map(([slot]) => ({ slot, title: "Saved" })),
put: async (slot, _title, data) => slots.set(slot, structuredClone(data)),
get: async (slot) => structuredClone(slots.get(slot)), remove: async (slot) => slots.delete(slot) };
window.native = new NativeTomie.TomiePlayer(document.querySelector("#host"), content, { versionId: 1, revision: "test", files: {} },
api, { line: (_tid, text) => text, str: (text) => text }, () => {});
window.slots = slots; window.native.start();
});
await page.locator(".tomie-dialogue").click();
await page.getByRole("button", { name: "Сохранить", exact: true }).click();
await page.getByRole("button", { name: "quick · Пусто", exact: true }).click();
assert.equal(await page.evaluate(() => window.slots.get("quick").runtime.stack.length), 1);
await page.locator(".tomie-dialogue").click();
await page.locator("dialog input").fill("Player");
await page.getByRole("button", { name: "Продолжить", exact: true }).click();
await page.getByRole("button", { name: "Choose", exact: true }).click();
assert.equal(await page.locator(".tomie-text").textContent(), "Saved Player");
assert.equal(await page.locator(".tomie-drop").count(), 140);
await page.getByRole("button", { name: "Загрузить", exact: true }).click();
await page.getByRole("button", { name: "quick · Saved", exact: true }).click();
assert.equal(await page.locator(".tomie-text").textContent(), "Subroutine");
await page.locator(".tomie-dialogue").click();
await page.locator("dialog input").fill("Restored");
await page.getByRole("button", { name: "Продолжить", exact: true }).click();
await page.getByRole("button", { name: "Choose", exact: true }).click();
await page.locator(".tomie-dialogue").click();
assert.match(await page.locator(".tomie-board").textContent(), /День 1/);
await page.getByRole("button", { name: "Инвентарь", exact: true }).click();
await page.getByRole("button", { name: "coin ×1", exact: true }).click();
assert.equal(await page.locator(".tomie-text").textContent(), "Used coin");
assert.equal(await page.evaluate(() => window.native.vm.evaluator.state.inventory.coin), 0);
await page.locator(".tomie-dialogue").click();
await page.getByRole("button", { name: "Начать", exact: true }).click();
await page.evaluate(() => window.native.mini.update(0.5));
await page.getByRole("button", { name: "Сохранить", exact: true }).click();
await page.getByRole("button", { name: "8 · Пусто", exact: true }).click();
assert.ok(await page.evaluate(() => window.slots.get("8").mini.elapsed >= 0.5));
await page.evaluate(() => {
for (let step = 0; step < 1300; step++) { window.native.mini.pulse(); window.native.mini.update(1 / 60); }
window.native.drawMini();
});
await page.getByRole("button", { name: "Продолжить", exact: true }).click();
assert.equal(await page.locator(".tomie-text").textContent(), "Won");
await page.getByRole("button", { name: "Загрузить", exact: true }).click();
await page.getByRole("button", { name: "8 · Saved", exact: true }).click();
assert.equal(await page.evaluate(() => window.native.mini.state.result), "running");
assert.equal(await page.locator(".tomie-mini").isVisible(), true);
assert.deepEqual(faults, []);
await page.evaluate(() => window.native.destroy());
assert.equal(await page.locator(".tomie-root").count(), 0);
} finally { await browser.close(); }
});