fix: update e2e tests for localhost and current game architecture
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m20s
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:
@@ -0,0 +1,209 @@
|
||||
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 12 - Debug Tab & Cheats
|
||||
// =========================================================================
|
||||
test.describe('12 - Debug Tab & Cheats', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await startFreshGame(page);
|
||||
});
|
||||
|
||||
test('navigate to Debug 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, 'debug');
|
||||
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 Debug: ${JSON.stringify(reactErrors)}`).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('Debug tab shows Game State section by default', async ({ page }) => {
|
||||
await waitForMs(page, 500);
|
||||
const visited = await clickTab(page, 'debug');
|
||||
if (visited) {
|
||||
const bodyText = await page.textContent('body') || '';
|
||||
expect(bodyText).toContain('Game State');
|
||||
}
|
||||
});
|
||||
|
||||
test('Debug tab has Mana Debug buttons', async ({ page }) => {
|
||||
await waitForMs(page, 500);
|
||||
const visited = await clickTab(page, 'debug');
|
||||
if (visited) {
|
||||
const fillBtn = page.getByTestId('debug-mana-fill');
|
||||
await expect(fillBtn).toBeVisible({ timeout: 5000 });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// SECTION 13 - Deep Bug Hunting with Debug Bridge
|
||||
// =========================================================================
|
||||
test.describe('13 - Deep Bug Hunting (Debug Bridge)', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await startFreshGame(page);
|
||||
await waitForBridge(page);
|
||||
});
|
||||
|
||||
test('mana regen values in ManaDisplay are correct', async ({ page }) => {
|
||||
await waitForMs(page, 1000);
|
||||
const bodyText = await page.textContent('body') || '';
|
||||
// Check that mana regen shows positive values for Transference
|
||||
const matches = bodyText.match(/\+[\d.]+(\/hr)?/g);
|
||||
console.log(`HUNT: Found regen patterns: ${JSON.stringify(matches)}`);
|
||||
// Should have at least one positive regen value
|
||||
expect(matches && matches.length > 0).toBe(true);
|
||||
});
|
||||
|
||||
test('mana values stay consistent after multiple ticks', async ({ page }) => {
|
||||
await waitForMs(page, 500);
|
||||
// Run 100 ticks via the bridge
|
||||
await page.evaluate(() => {
|
||||
(window as any).__TEST__.runTicks(100);
|
||||
});
|
||||
await waitForMs(page, 500);
|
||||
// Game should still be running (no crash)
|
||||
const bodyAfter = await page.textContent('body') || '';
|
||||
expect(bodyAfter).toBeTruthy();
|
||||
console.log('HUNT: Game still running after 100 ticks ✓');
|
||||
});
|
||||
|
||||
test('debug bridge can read all store states', async ({ page }) => {
|
||||
const storeKeys = await page.evaluate(() => {
|
||||
const t = (window as any).__TEST__;
|
||||
return {
|
||||
hasGameStore: !!t.useGameStore,
|
||||
hasManaStore: !!t.useManaStore,
|
||||
hasCombatStore: !!t.useCombatStore,
|
||||
hasCraftingStore: !!t.useCraftingStore,
|
||||
hasAttunementStore: !!t.useAttunementStore,
|
||||
hasPrestigeStore: !!t.usePrestigeStore,
|
||||
hasDisciplineStore: !!t.useDisciplineStore,
|
||||
hasUIStore: !!t.useUIStore,
|
||||
hasRunTicks: typeof t.runTicks === 'function',
|
||||
};
|
||||
});
|
||||
expect(storeKeys.hasGameStore).toBe(true);
|
||||
expect(storeKeys.hasManaStore).toBe(true);
|
||||
expect(storeKeys.hasCombatStore).toBe(true);
|
||||
expect(storeKeys.hasCraftingStore).toBe(true);
|
||||
expect(storeKeys.hasAttunementStore).toBe(true);
|
||||
expect(storeKeys.hasPrestigeStore).toBe(true);
|
||||
expect(storeKeys.hasDisciplineStore).toBe(true);
|
||||
expect(storeKeys.hasUIStore).toBe(true);
|
||||
expect(storeKeys.hasRunTicks).toBe(true);
|
||||
});
|
||||
|
||||
test('debug bridge runTicks advances game time', async ({ page }) => {
|
||||
const dayBefore = await page.evaluate(() =>
|
||||
(window as any).__TEST__.useGameStore.getState().day
|
||||
);
|
||||
await page.evaluate(() => {
|
||||
(window as any).__TEST__.runTicks(1200); // ~1 day
|
||||
});
|
||||
const dayAfter = await page.evaluate(() =>
|
||||
(window as any).__TEST__.useGameStore.getState().day
|
||||
);
|
||||
expect(dayAfter).toBeGreaterThanOrEqual(dayBefore);
|
||||
});
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// SECTION 14 - All Tabs Navigation Stress Test
|
||||
// =========================================================================
|
||||
test.describe('14 - All Tabs Navigation Stress Test', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await startFreshGame(page);
|
||||
});
|
||||
|
||||
test('all navigations work in sequence without crash', async ({ page }) => {
|
||||
const errors: string[] = [];
|
||||
page.on('console', (msg) => {
|
||||
if (msg.type() === 'error') errors.push(msg.text());
|
||||
});
|
||||
await waitForMs(page, 500);
|
||||
|
||||
const tabs = [
|
||||
'stats', 'disciplines', 'debug', 'attunements', 'achievements',
|
||||
'prestige', 'equipment', 'golemancy', 'pacts', 'spire', 'crafting',
|
||||
];
|
||||
|
||||
const visitedTabs: string[] = [];
|
||||
const crashTabs: string[] = [];
|
||||
|
||||
for (const tabName of tabs) {
|
||||
const tab = page.getByRole('tab', { name: new RegExp(tabName, 'i') });
|
||||
if (await tab.isVisible({ timeout: 2000 })) {
|
||||
const preErrors = [...errors];
|
||||
await tab.click();
|
||||
await waitForMs(page, 300);
|
||||
const newErrors = errors.filter(e => !preErrors.includes(e));
|
||||
const reactErrors = newErrors.filter(e =>
|
||||
e.includes('React') || e.includes('Minified') || e.includes('Error #')
|
||||
);
|
||||
if (reactErrors.length > 0) {
|
||||
crashTabs.push(tabName);
|
||||
}
|
||||
visitedTabs.push(tabName);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`HUNT: Visited tabs: ${visitedTabs.join(', ')}`);
|
||||
console.log(`HUNT: Tabs with React errors: ${crashTabs.join(', ')}`);
|
||||
|
||||
// All tabs should be visitable without React errors
|
||||
expect(crashTabs, `Tabs with React errors: ${JSON.stringify(crashTabs)}`).toHaveLength(0);
|
||||
// Should have visited all 11 tabs
|
||||
expect(visitedTabs.length).toBe(11);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user