SPB Git forge

spb/spinza

Public
8commits 1branches 0releases
1.6 MBsize
maindefault branch
16 days agolast push
TypeScript 97.6% SQL 1.4% JavaScript 0.5%
4.2 KB · 110 lines javascript
Raw Blame History
1/**2 * End-to-end smoke test with Playwright (uses a globally installed playwright if present).3 *   BASE=https://www.spinza.dev node qa/e2e.mjs4 * Flow: landing → register → recovery code → lobby → open Neon Vault → spin → rewards → leaderboard → profile → mobile viewport.5 * Screenshots land in qa/screens/.6 */7import fs from "node:fs";8import path from "node:path";9import { createRequire } from "node:module";1011const require = createRequire(import.meta.url);12let chromium;13try {14  ({ chromium } = require("playwright"));15} catch {16  ({ chromium } = require(path.join(process.env.HOME ?? "", "Desktop/Projets/apps-web/fetcha/qa/node_modules/playwright")));17}1819const BASE = (process.env.BASE ?? "http://localhost:8230").replace(/\/$/, "");20const out = path.resolve("qa/screens");21fs.mkdirSync(out, { recursive: true });22const shot = (page, name) => page.screenshot({ path: path.join(out, `${name}.png`), fullPage: false });2324const username = `qa_${Date.now().toString(36).slice(-6)}`;25const password = "qa-password-123";26let failures = 0;27const step = async (name, fn) => {28  try {29    await fn();30    console.log(`✓ ${name}`);31  } catch (e) {32    failures++;33    console.log(`✗ ${name}: ${e.message}`);34  }35};3637const browser = await chromium.launch();38const ctx = await browser.newContext({ viewport: { width: 1380, height: 900 } });39const page = await ctx.newPage();40page.on("pageerror", (e) => console.log("  pageerror:", e.message));4142await step("landing renders", async () => {43  await page.goto(BASE, { waitUntil: "networkidle" });44  if (!(await page.getByText(/PLAY FREE/i).first().isVisible())) throw new Error("no CTA");45  await shot(page, "01-landing");46});4748await step("register + recovery code", async () => {49  await page.goto(`${BASE}/register`, { waitUntil: "networkidle" });50  await page.getByLabel(/username/i).fill(username);51  const pw = page.locator('input[type="password"]');52  await pw.nth(0).fill(password);53  await pw.nth(1).fill(password);54  await page.getByRole("checkbox").first().check();55  await page.getByRole("button", { name: /create account/i }).click();56  await page.getByText(/SPZ-[A-Z2-9]{4}-[A-Z2-9]{4}-[A-Z2-9]{4}/).waitFor({ timeout: 15000 });57  await shot(page, "02-recovery-code");58  const cb = page.getByRole("checkbox");59  if (await cb.count()) await cb.last().check();60  await page.getByRole("button", { name: /continue/i }).click();61  await page.waitForURL(/\/(\?welcome=1)?$/, { timeout: 15000 });62});6364await step("lobby shows balance 10,000 SC", async () => {65  await page.waitForLoadState("networkidle");66  await page.getByText(/10,000/).first().waitFor({ timeout: 15000 });67  await shot(page, "03-lobby");68});6970await step("open Neon Vault and spin", async () => {71  await page.goto(`${BASE}/games/neon-vault`, { waitUntil: "networkidle" });72  const spin = page.getByRole("button", { name: /^spin$/i });73  await spin.waitFor({ state: "visible", timeout: 20000 });74  await page.waitForTimeout(1500);75  await spin.click();76  await page.waitForTimeout(4500);77  await shot(page, "04-game");78  const bal = await page.locator("text=/\\d{1,3}(,\\d{3})*/").first().textContent();79  if (!bal) throw new Error("no balance");80});8182await step("rewards page", async () => {83  await page.goto(`${BASE}/rewards`, { waitUntil: "networkidle" });84  await page.getByText(/daily/i).first().waitFor();85  await shot(page, "05-rewards");86});8788await step("leaderboard + profile", async () => {89  await page.goto(`${BASE}/leaderboard`, { waitUntil: "networkidle" });90  await shot(page, "06-leaderboard");91  await page.goto(`${BASE}/profile`, { waitUntil: "networkidle" });92  await page.getByText(username).first().waitFor();93  await shot(page, "07-profile");94});9596await step("mobile lobby + game", async () => {97  const m = await browser.newContext({ viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true, storageState: await ctx.storageState() });98  const mp = await m.newPage();99  await mp.goto(BASE, { waitUntil: "networkidle" });100  await shot(mp, "08-mobile-lobby");101  await mp.goto(`${BASE}/games/spinza-original`, { waitUntil: "networkidle" });102  await mp.waitForTimeout(2500);103  await shot(mp, "09-mobile-game");104  await m.close();105});106107await browser.close();108console.log(failures ? `${failures} step(s) failed` : "all steps passed");109process.exit(failures ? 1 : 0);110