// 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('
'); 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(); } });