import { test, expect, type Page } from '@playwright/test'; test.use({ baseURL: 'http://localhost:3000/', }); // ─── Helpers ───────────────────────────────────────────────────────────────── async function waitForMs(page: Page, ms: number) { await page.waitForTimeout(ms); } async function startFreshGame(page: Page) { await page.goto('/'); await page.evaluate(() => localStorage.clear()); await page.reload(); await page.waitForLoadState('networkidle'); await waitForMs(page, 3000); } async function waitForBridge(page: Page) { for (let attempt = 0; attempt < 30; attempt++) { const ready = await page.evaluate(() => !!(window as any).__TEST__); if (ready) return; await waitForMs(page, 1000); } throw new Error('Debug bridge (window.__TEST__) not available after 30s'); } async function clickTab(page: Page, label: string) { const tab = page.getByRole('tab', { name: new RegExp(label, 'i') }).first(); if (await tab.isVisible({ timeout: 2000 })) { await tab.click(); await waitForMs(page, 400); return true; } return false; } // ─── Test Suite ────────────────────────────────────────────────────────────── test.describe('Mana Loop - Comprehensive Playtest', () => { // ========================================================================= // SECTION 4 - Disciplines Tab // ========================================================================= test.describe('4 - Disciplines', () => { test.beforeEach(async ({ page }) => { await startFreshGame(page); }); test('navigate to Disciplines tab without crash', async ({ page }) => { const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); await waitForMs(page, 500); const visited = await clickTab(page, 'disciplines'); expect(visited).toBe(true); await waitForMs(page, 500); const reactErrors = errors.filter(e => e.includes('React') || e.includes('Minified') || e.includes('Error #') ); expect(reactErrors, `React errors in Disciplines: ${JSON.stringify(reactErrors)}`).toHaveLength(0); }); test('Raw Mana Mastery discipline is available', async ({ page }) => { await waitForMs(page, 500); const visited = await clickTab(page, 'disciplines'); if (visited) { const bodyText = await page.textContent('body') || ''; expect(bodyText).toContain('Raw Mana Mastery'); } }); }); // ========================================================================= // SECTION 5 - Crafting Tab // ========================================================================= test.describe('5 - Crafting System', () => { test.beforeEach(async ({ page }) => { await startFreshGame(page); }); test('navigate to Crafting tab without crash', async ({ page }) => { const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); await waitForMs(page, 500); const visited = await clickTab(page, 'craft'); expect(visited).toBe(true); await waitForMs(page, 500); const reactErrors = errors.filter(e => e.includes('React') || e.includes('Minified') || e.includes('Error #') ); expect(reactErrors, `React errors in Crafting: ${JSON.stringify(reactErrors)}`).toHaveLength(0); }); test('Enchanter and Fabricator sub-tabs exist', async ({ page }) => { await waitForMs(page, 500); const visited = await clickTab(page, 'craft'); if (visited) { const bodyText = await page.textContent('body') || ''; expect(bodyText).toContain('Enchanter'); expect(bodyText).toContain('Fabricator'); } }); }); // ========================================================================= // SECTION 6 - Equipment Tab // ========================================================================= test.describe('6 - Equipment & Inventory', () => { test.beforeEach(async ({ page }) => { await startFreshGame(page); }); test('navigate to Equipment tab without crash', async ({ page }) => { const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); await waitForMs(page, 500); const visited = await clickTab(page, 'equipment'); expect(visited).toBe(true); await waitForMs(page, 500); const reactErrors = errors.filter(e => e.includes('React') || e.includes('Minified') || e.includes('Error #') ); expect(reactErrors, `React errors in Equipment: ${JSON.stringify(reactErrors)}`).toHaveLength(0); }); test('starting equipment includes Basic Staff, Civilian Shirt, Civilian Shoes', async ({ page }) => { await waitForMs(page, 500); const visited = await clickTab(page, 'equipment'); if (visited) { const bodyText = await page.textContent('body') || ''; expect(bodyText).toContain('Basic Staff'); expect(bodyText).toContain('Civilian Shirt'); expect(bodyText).toContain('Civilian Shoes'); } }); }); // ========================================================================= // SECTION 7 - Attunements Tab // ========================================================================= test.describe('7 - Attunements', () => { test.beforeEach(async ({ page }) => { await startFreshGame(page); }); test('navigate to Attunements tab without crash', async ({ page }) => { const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); await waitForMs(page, 500); const visited = await clickTab(page, 'attun'); expect(visited).toBe(true); await waitForMs(page, 500); const reactErrors = errors.filter(e => e.includes('React') || e.includes('Minified') || e.includes('Error #') ); expect(reactErrors, `React errors in Attunements: ${JSON.stringify(reactErrors)}`).toHaveLength(0); }); test('Enchanter is attuned at level 1 by default', async ({ page }) => { await waitForMs(page, 500); const visited = await clickTab(page, 'attun'); if (visited) { const bodyText = await page.textContent('body') || ''; expect(bodyText).toContain('Enchanter'); } }); }); // ========================================================================= // SECTION 8 - Prestige Tab // ========================================================================= test.describe('8 - Prestige Tab', () => { test.beforeEach(async ({ page }) => { await startFreshGame(page); }); test('navigate to Prestige tab without crash', async ({ page }) => { const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); await waitForMs(page, 500); const visited = await clickTab(page, 'prestige'); expect(visited).toBe(true); await waitForMs(page, 500); const reactErrors = errors.filter(e => e.includes('React') || e.includes('Minified') || e.includes('Error #') ); expect(reactErrors, `React errors in Prestige: ${JSON.stringify(reactErrors)}`).toHaveLength(0); }); }); // ========================================================================= // SECTION 9 - Golemancy Tab // ========================================================================= test.describe('9 - Golemancy Tab', () => { test.beforeEach(async ({ page }) => { await startFreshGame(page); }); test('navigate to Golemancy tab without crash', async ({ page }) => { const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); await waitForMs(page, 500); const visited = await clickTab(page, 'golem'); expect(visited).toBe(true); await waitForMs(page, 500); const reactErrors = errors.filter(e => e.includes('React') || e.includes('Minified') || e.includes('Error #') ); expect(reactErrors, `React errors in Golemancy: ${JSON.stringify(reactErrors)}`).toHaveLength(0); }); }); // ========================================================================= // SECTION 10 - Guardian Pacts Tab // ========================================================================= test.describe('10 - Guardian Pacts Tab', () => { test.beforeEach(async ({ page }) => { await startFreshGame(page); }); test('navigate to Guardian Pacts tab without crash', async ({ page }) => { const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); await waitForMs(page, 500); const visited = await clickTab(page, 'pact'); expect(visited).toBe(true); await waitForMs(page, 500); const reactErrors = errors.filter(e => e.includes('React') || e.includes('Minified') || e.includes('Error #') ); expect(reactErrors, `React errors in Guardian Pacts: ${JSON.stringify(reactErrors)}`).toHaveLength(0); }); }); // ========================================================================= // SECTION 11 - Achievements Tab // ========================================================================= test.describe('11 - Achievements Tab', () => { test.beforeEach(async ({ page }) => { await startFreshGame(page); }); test('navigate to Achievements tab without crash', async ({ page }) => { const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); await waitForMs(page, 500); const visited = await clickTab(page, 'achievement'); expect(visited).toBe(true); await waitForMs(page, 500); const reactErrors = errors.filter(e => e.includes('React') || e.includes('Minified') || e.includes('Error #') ); expect(reactErrors, `React errors in Achievements: ${JSON.stringify(reactErrors)}`).toHaveLength(0); }); }); });