Add multiplayer rooms and Vercel analytics
This commit is contained in:
181
scripts/test-multiplayer-browser.mjs
Normal file
181
scripts/test-multiplayer-browser.mjs
Normal file
@@ -0,0 +1,181 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import { chromium, expect } from "@playwright/test";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
if (fs.existsSync(".env.local")) process.loadEnvFile(".env.local");
|
||||
const origin = new URL(process.argv[2] || "http://127.0.0.1:3007").origin;
|
||||
const db = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.SUPABASE_SECRET_KEY,
|
||||
{ auth: { persistSession: false, autoRefreshToken: false } },
|
||||
);
|
||||
const rooms = [];
|
||||
const browser = await chromium.launch({
|
||||
headless: true,
|
||||
args: ["--use-angle=swiftshader", "--enable-unsafe-swiftshader"],
|
||||
});
|
||||
try {
|
||||
const { data: maps, error } = await db
|
||||
.from("datasets")
|
||||
.select("id")
|
||||
.eq("state", "published")
|
||||
.limit(1);
|
||||
if (error) throw error;
|
||||
assert.ok(maps.length);
|
||||
const desktop = await browser.newContext({
|
||||
viewport: { width: 1280, height: 900 },
|
||||
locale: "en-US",
|
||||
});
|
||||
const mobile = await browser.newContext({
|
||||
viewport: { width: 390, height: 844 },
|
||||
locale: "en-US",
|
||||
isMobile: true,
|
||||
hasTouch: true,
|
||||
});
|
||||
const host = await desktop.newPage(),
|
||||
friend = await mobile.newPage();
|
||||
const pageErrors = [];
|
||||
for (const page of [host, friend])
|
||||
page.on("pageerror", (error) => pageErrors.push(error.message));
|
||||
await host.goto(`${origin}/servers/${maps[0].id}`, {
|
||||
waitUntil: "networkidle",
|
||||
timeout: 60000,
|
||||
});
|
||||
await host.getByRole("button", { name: "Multiplayer", exact: true }).click();
|
||||
await host.getByLabel("Your player name").fill("Browser Host");
|
||||
await host.getByLabel("Player limit (including you)").selectOption("2");
|
||||
await host
|
||||
.getByRole("button", { name: "Create multiplayer room", exact: true })
|
||||
.click();
|
||||
await host.waitForURL("**/multiplayer/*", { timeout: 30000 });
|
||||
const id = host.url().split("/").pop();
|
||||
rooms.push(id);
|
||||
await expect(host.getByLabel("Invite link")).toHaveValue(
|
||||
`${origin}/multiplayer/${id}`,
|
||||
);
|
||||
await friend.goto(host.url(), {
|
||||
waitUntil: "domcontentloaded",
|
||||
timeout: 60000,
|
||||
});
|
||||
await friend.getByLabel("Your player name").fill("Browser Friend");
|
||||
await friend.getByRole("button", { name: "Join room", exact: true }).click();
|
||||
await expect(
|
||||
friend.getByText("You’re in! Waiting for the room owner to start.", {
|
||||
exact: true,
|
||||
}),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
host.getByRole("button", { name: "Start multiplayer match", exact: true }),
|
||||
).toBeEnabled({ timeout: 20000 });
|
||||
fs.mkdirSync("artifacts/multiplayer", { recursive: true });
|
||||
await host.screenshot({
|
||||
path: "artifacts/multiplayer/desktop-lobby.png",
|
||||
fullPage: true,
|
||||
});
|
||||
await friend.screenshot({
|
||||
path: "artifacts/multiplayer/mobile-lobby.png",
|
||||
fullPage: true,
|
||||
});
|
||||
assert.ok(
|
||||
await friend.evaluate(
|
||||
() => document.documentElement.scrollWidth <= window.innerWidth,
|
||||
),
|
||||
"mobile lobby fits viewport",
|
||||
);
|
||||
await host
|
||||
.getByRole("button", { name: "Start multiplayer match", exact: true })
|
||||
.click();
|
||||
for (let round = 0; round < 5; round++) {
|
||||
await Promise.all([
|
||||
expect(host.locator(".game-info strong").first()).toHaveText(
|
||||
`${round + 1} / 5`,
|
||||
{ timeout: 30000 },
|
||||
),
|
||||
expect(friend.locator(".game-info strong").first()).toHaveText(
|
||||
`${round + 1} / 5`,
|
||||
{ timeout: 30000 },
|
||||
),
|
||||
]);
|
||||
await Promise.all([
|
||||
expect(host.locator(".game-loading")).toHaveCount(0, { timeout: 45000 }),
|
||||
expect(friend.locator(".game-loading")).toHaveCount(0, {
|
||||
timeout: 45000,
|
||||
}),
|
||||
]);
|
||||
if (round === 0) {
|
||||
await host.reload({ waitUntil: "domcontentloaded" });
|
||||
await expect(host.locator(".game-loading")).toHaveCount(0, {
|
||||
timeout: 45000,
|
||||
});
|
||||
}
|
||||
await friend
|
||||
.getByRole("button", { name: "Open guess map", exact: true })
|
||||
.click();
|
||||
for (const [page, fraction] of [
|
||||
[host, 0.48],
|
||||
[friend, 0.7],
|
||||
]) {
|
||||
const map = page.getByRole("application", { name: /Minecraft map/ });
|
||||
const box = await map.boundingBox();
|
||||
assert.ok(box);
|
||||
await map.click({
|
||||
position: { x: box.width * fraction, y: box.height * 0.5 },
|
||||
});
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Submit guess", exact: true }),
|
||||
).toBeEnabled();
|
||||
if (round === 0 && page === host)
|
||||
await page.screenshot({
|
||||
path: "artifacts/multiplayer/desktop-round.png",
|
||||
});
|
||||
await page
|
||||
.getByRole("button", { name: "Submit guess", exact: true })
|
||||
.click();
|
||||
}
|
||||
if (round < 4) {
|
||||
await expect(
|
||||
host.getByRole("heading", { name: "Round standings", exact: true }),
|
||||
).toBeVisible({ timeout: 20000 });
|
||||
await expect(
|
||||
friend.getByRole("heading", { name: "Round standings", exact: true }),
|
||||
).toBeVisible({ timeout: 20000 });
|
||||
await host.getByRole("button", { name: /^Next round/ }).click();
|
||||
}
|
||||
console.log(`Browser round ${round + 1}/5 completed.`);
|
||||
}
|
||||
await expect(
|
||||
host.getByRole("heading", { name: "Final standings", exact: true }),
|
||||
).toBeVisible({ timeout: 20000 });
|
||||
await expect(
|
||||
friend.getByRole("heading", { name: "Final standings", exact: true }),
|
||||
).toBeVisible({ timeout: 20000 });
|
||||
assert.equal(await host.locator(".multiplayer-final tbody tr").count(), 2);
|
||||
await host.screenshot({
|
||||
path: "artifacts/multiplayer/desktop-results.png",
|
||||
fullPage: true,
|
||||
});
|
||||
await friend.screenshot({
|
||||
path: "artifacts/multiplayer/mobile-results.png",
|
||||
fullPage: true,
|
||||
});
|
||||
assert.ok(
|
||||
await friend.evaluate(
|
||||
() => document.documentElement.scrollWidth <= window.innerWidth,
|
||||
),
|
||||
"mobile results fit viewport with table scrolling",
|
||||
);
|
||||
assert.deepEqual(pageErrors, []);
|
||||
console.log(
|
||||
"Desktop/mobile browser multiplayer passed: create, invite, join, reconnect, five shared rounds, map guesses, and final standings. No page errors.",
|
||||
);
|
||||
} finally {
|
||||
await browser.close();
|
||||
if (rooms.length) {
|
||||
const { error } = await db
|
||||
.from("multiplayer_rooms")
|
||||
.delete()
|
||||
.in("id", rooms);
|
||||
if (error)
|
||||
throw new Error(`Browser test-room cleanup failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user