import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { createServer } from "node:http"; import { fileURLToPath, pathToFileURL } from "node:url"; import { test } from "node:test"; import { build } from "esbuild"; test("the owner can toggle the gallery, everyone sees its badge and direct entry", { skip: !process.env.PGAMES_PLAYWRIGHT_PATH, }, async () => { const { chromium } = await import(pathToFileURL(process.env.PGAMES_PLAYWRIGHT_PATH + "/index.mjs").href); const compiled = await build({ stdin: { contents: ` import {createElement as h} from 'react'; import {createRoot} from 'react-dom/client'; import {MemoryRouter, Routes, Route, useLocation} from 'react-router-dom'; import {AuthProvider} from './auth'; import {UiProvider} from './ui/Ui'; import {TranslationRunProvider} from './translator/runner'; import {UploadProvider} from './uploads'; import {GamePage} from './pages/GamePage'; function Destination() {const at = useLocation(); return h('output', null, at.pathname + at.search);} createRoot(document.querySelector('main')).render(h(MemoryRouter, {initialEntries: ['/games/1']}, h(UiProvider, null, h(AuthProvider, null, h(TranslationRunProvider, null, h(UploadProvider, null, h(Routes, null, h(Route, {path: '/games/:gameId', element: h(GamePage)}), h(Route, {path: '/play/:versionId', element: h(Destination)}))))))));`, resolveDir: fileURLToPath(new URL("../src/", import.meta.url)), loader: "tsx" }, bundle: true, write: false, format: "iife", platform: "browser", logLevel: "silent" }); const css = readFileSync(new URL("../src/styles.css", import.meta.url), "utf8"); const version = { id: 1, game_id: 1, parent_id: null, owner: { id: 7, username: "author" }, name: "Моя версия", status: "ready", kind: "mod", visibility: "draft", created_at: new Date().toISOString(), size_bytes: 1, gallery_available: true, gallery_unlocked: false, languages: [] }; let userId = 7, fail = false; const writes = []; const server = createServer(async (req, res) => { if (req.url === "/bundle.js") { res.setHeader("Content-Type", "text/javascript"); res.end(compiled.outputFiles[0].text); return; } if (req.url === "/style.css") { res.setHeader("Content-Type", "text/css"); res.end(css); return; } res.setHeader("Content-Type", "application/json"); if (req.url === "/api/auth/me") { res.end(JSON.stringify({ id: userId, username: "author", permissions: ["games.play"] })); return; } if (req.url === "/api/games/1") { res.end(JSON.stringify({ id: 1, title: "MIST", tags: [], gallery_hashes: [], versions: [version] })); return; } if (req.url === "/api/versions/1/gallery" && req.method === "PATCH") { let body = ""; for await (const chunk of req) body += chunk; const settings = JSON.parse(body); writes.push(settings); if (fail) { res.statusCode = 500; res.end(JSON.stringify({ detail: "Не удалось сохранить настройку" })); return; } version.gallery_unlocked = settings.gallery_unlocked; res.end(JSON.stringify(version)); return; } res.setHeader("Content-Type", "text/html"); res.end('
'); }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); let browser; try { browser = await chromium.launch({ headless: true, channel: process.env.PGAMES_BROWSER_CHANNEL }); const page = await browser.newPage({ viewport: { width: 1100, height: 800 } }); page.setDefaultTimeout(5000); const origin = `http://127.0.0.1:${server.address().port}`; await page.goto(origin); const checkbox = page.getByRole("checkbox", { name: "Открыть всю галерею в этой версии" }); await checkbox.check(); await page.getByText("Галерея открыта", { exact: true }).waitFor(); await page.getByRole("button", { name: "Открыть галерею", exact: true }).click(); await page.getByText("/play/1?gallery=1", { exact: true }).waitFor(); await page.reload(); await checkbox.waitFor(); assert.equal(await checkbox.isChecked(), true); await checkbox.uncheck(); await page.getByText("Галерея открыта", { exact: true }).waitFor({ state: "detached" }); fail = true; await checkbox.click(); await page.getByText("Не удалось сохранить настройку", { exact: true }).waitFor(); assert.equal(await checkbox.isChecked(), false); assert.deepEqual(writes, [{ gallery_unlocked: true }, { gallery_unlocked: false }, { gallery_unlocked: true }]); version.gallery_unlocked = true; userId = 8; await page.reload(); await page.getByText("Галерея открыта", { exact: true }).waitFor(); assert.equal(await checkbox.count(), 0); await page.setViewportSize({ width: 375, height: 800 }); assert.equal(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), true); } finally { await browser?.close(); await new Promise((resolve) => server.close(resolve)); } }); test("the unlocked Gallery viewer opens above its index and closes back to the main menu", { skip: !process.env.PGAMES_PLAYWRIGHT_PATH, }, async () => { const { chromium } = await import(pathToFileURL(process.env.PGAMES_PLAYWRIGHT_PATH + "/index.mjs").href); const compiled = await build({ stdin: { contents: ` import {Engine} from './player/engine'; import {parseStatements} from './player/pyparse'; const game = {resolution: [640, 480], entry: 'start', title: 'Gallery test', ui: {}, files: {}, defaults: [], init: parseStatements('g = Gallery()\\ng.button("Art")\\ng.image("picture")\\ng.condition("False")'), characters: {}, channels: {}, images: {picture: {color: '#ff00ff'}}, transforms: {}, atl: {}, transitions: {}, screens: {}, styles: {}, audio: {}}; const tr = {lang: 'en', renpyName: null, str: s => s, line: (_, s) => s}; const saves = {list: async () => [], get: async () => null, put: async () => {}, remove: async () => {}}; const engine = new Engine(document.querySelector('main'), game, {files: {}, galleryUnlocked: true}, [{labels: [{name: 'start', body: [{op: 'return'}]}], anchors: {}}], saves, 'gallery-browser', () => {}, tr, async () => tr); engine.start();`, resolveDir: fileURLToPath(new URL("../src/", import.meta.url)), loader: "tsx" }, bundle: true, write: false, format: "iife", platform: "browser", logLevel: "silent" }); const css = readFileSync(new URL("../src/player/player.css", import.meta.url), "utf8"); const server = createServer((req, res) => { if (req.url === "/bundle.js") { res.setHeader("Content-Type", "text/javascript"); res.end(compiled.outputFiles[0].text); return; } if (req.url === "/style.css") { res.setHeader("Content-Type", "text/css"); res.end(css); return; } res.setHeader("Content-Type", "text/html"); res.end('
'); }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); let browser; try { browser = await chromium.launch({ headless: true, channel: process.env.PGAMES_BROWSER_CHANNEL }); const page = await browser.newPage({ viewport: { width: 1000, height: 800 } }); page.setDefaultTimeout(5000); await page.goto(`http://127.0.0.1:${server.address().port}`); await page.getByRole("button", { name: "Галерея открыта", exact: true }).click(); await page.getByRole("button", { name: "Art", exact: true }).click(); await page.locator(".pg-gallery").waitFor(); const top = await page.locator(".pg-gallery").evaluate((overlay) => { const box = overlay.getBoundingClientRect(); return !!document.elementFromPoint(box.x + box.width / 2, box.y + box.height / 2)?.closest(".pg-gallery"); }); assert.equal(top, true, "the gallery image is above the index and receives input"); await page.locator(".pg-gallery").getByRole("button", { name: "Закрыть", exact: true }).click(); await page.getByRole("button", { name: "Art", exact: true }).waitFor(); await page.getByRole("button", { name: "Закрыть", exact: true }).click(); await page.getByRole("button", { name: "Начать", exact: true }).waitFor(); } finally { await browser?.close(); await new Promise((resolve) => server.close(resolve)); } });