Files
pgames-manager/frontend/tests/version-gallery-browser.test.mjs
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

131 lines
8.5 KiB
JavaScript

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('<!doctype html><meta charset="utf-8"><link rel="stylesheet" href="/style.css"><main></main><script src="/bundle.js"></script>');
});
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('<!doctype html><meta charset="utf-8"><link rel="stylesheet" href="/style.css"><style>main{width:800px;height:600px}</style><main></main><script src="/bundle.js"></script>');
});
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));
}
});