fix: update e2e tests for localhost and current game architecture
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m20s

- playwright.config.ts: change baseURL from dev site to localhost:3000
- combat-happy-path.spec.ts: fix climb button location (LeftPanel, not spire tab), fix descent via store, handle game-over from day overflow, reduce tick counts to avoid day 30 limit
- fabricator-happy-path.spec.ts: set currentAction to meditate before crafting (required by startFabricatorCrafting)
- playtest.spec.ts: rewrite from scratch — use localhost, window.__TEST__ bridge (not window.__debug), current tab names (no grimoire/element tabs), split into 3 files under 400-line limit
  - playtest-basic-ui.spec.ts: sections 1-3 (basic UI, stats, spire)
  - playtest-tabs.spec.ts: sections 4-11 (all tab navigation tests)
  - playtest-debug.spec.ts: sections 12-14 (debug tab, bridge, stress test)
This commit is contained in:
2026-06-11 16:09:44 +02:00
parent ae8d669c71
commit 8b41f137d5
10 changed files with 771 additions and 674 deletions
+152
View File
@@ -0,0 +1,152 @@
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 1: Basic UI & Starting State
// =========================================================================
test.describe('1 - Basic UI & Starting State', () => {
test.beforeEach(async ({ page }) => {
await startFreshGame(page);
});
test('game loads without console errors', async ({ page }) => {
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') errors.push(msg.text());
});
await waitForMs(page, 2000);
const reactErrors = errors.filter(e =>
e.includes('React') || e.includes('Minified') || e.includes('Error #') || e.includes('Maximum update depth')
);
expect(reactErrors, `React errors found: ${JSON.stringify(reactErrors)}`).toHaveLength(0);
});
test('ManaDisplay is visible and shows Transference mana', async ({ page }) => {
await waitForMs(page, 500);
// Transference starts unlocked but with 0 current mana.
// The element section only shows elements with current > 0.
// Click Gather a few times to get raw mana, then check the display.
const gatherBtn = page.getByRole('button', { name: /gather/i }).first();
await expect(gatherBtn).toBeVisible({ timeout: 10000 });
// Gather some mana
for (let i = 0; i < 5; i++) {
await gatherBtn.click();
await waitForMs(page, 100);
}
// The raw mana display should be visible
const bodyText = await page.textContent('body') || '';
expect(bodyText).toContain('mana');
});
test('TimeDisplay shows correct starting time', async ({ page }) => {
await waitForMs(page, 500);
const bodyText = await page.textContent('body');
expect(bodyText).toContain('Day 1');
});
test('Activity log is present and shows start message', async ({ page }) => {
await waitForMs(page, 500);
const bodyText = await page.textContent('body');
expect(bodyText).toBeTruthy();
});
});
// =========================================================================
// SECTION 2 - Stats Tab
// =========================================================================
test.describe('2 - Stats Tab', () => {
test.beforeEach(async ({ page }) => {
await startFreshGame(page);
});
test('navigate to Stats tab', async ({ page }) => {
await waitForMs(page, 500);
const visited = await clickTab(page, 'stats');
expect(visited).toBe(true);
const bodyText = await page.textContent('body');
expect(bodyText).toBeTruthy();
});
test('Stats tab shows mana-related stats', async ({ page }) => {
await waitForMs(page, 500);
const visited = await clickTab(page, 'stats');
if (visited) {
const bodyText = await page.textContent('body') || '';
// Stats tab should show some mana-related content
expect(bodyText.length).toBeGreaterThan(100);
}
});
});
// =========================================================================
// SECTION 3 - Spire / Climbing
// =========================================================================
test.describe('3 - Spire / Climbing', () => {
test.beforeEach(async ({ page }) => {
await startFreshGame(page);
});
test('navigate to Spire 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, 'spire');
expect(visited).toBe(true);
await waitForMs(page, 500);
const reactErrors = errors.filter(e =>
e.includes('Maximum update depth') || e.includes('Error #185')
);
expect(reactErrors, `Spire tab errors: ${JSON.stringify(reactErrors)}`).toHaveLength(0);
});
test('Climb the Spire button is visible on main page', async ({ page }) => {
await waitForMs(page, 500);
const climbBtn = page.getByRole('button', { name: /climb the spire/i }).first();
await expect(climbBtn).toBeVisible({ timeout: 10000 });
});
});
});