/** * PokeRogue Extension E2E Test - v2 */ const { chromium } = require('playwright'); const path = require('path'); const fs = require('fs'); const EXTENSION_PATH = '/home/ceo/Desktop/poke-extension'; const USER_DATA_DIR = '/tmp/poke-ext-test-profile-' + Date.now(); const SCREENSHOT_DIR = '/home/ceo/Desktop/poke-extension'; const USERNAME = 'ClaudeCode'; const PASSWORD = '5454%colagenio%5656'; const consoleMessages = []; const extMessages = []; const sleep = ms => new Promise(r => setTimeout(r, ms)); async function shot(page, name) { const fp = path.join(SCREENSHOT_DIR, `test-${name}.png`); await page.screenshot({ path: fp }); console.log(`[SHOT] ${fp}`); } async function main() { console.log('[TEST] === PokeRogue Extension E2E Test v2 ==='); // Step 1: Launch console.log('\n[STEP 1] Launching Chromium with extension...'); const context = await chromium.launchPersistentContext(USER_DATA_DIR, { headless: false, args: [ '--ignore-gpu-blacklist', '--disable-gpu-sandbox', `--disable-extensions-except=${EXTENSION_PATH}`, `--load-extension=${EXTENSION_PATH}`, '--no-first-run', '--disable-blink-features=AutomationControlled', '--window-size=1280,900', ], viewport: { width: 1280, height: 900 }, ignoreDefaultArgs: ['--disable-extensions'], }); let page = context.pages()[0] || await context.newPage(); page.on('console', msg => { const t = msg.text(); consoleMessages.push({ type: msg.type(), text: t }); if (t.includes('[PokeRogue Ext]')) { extMessages.push(t); console.log(` [EXT] ${t}`); } }); page.on('pageerror', err => console.log(` [PERR] ${err.message.substring(0, 150)}`)); // Step 2: Navigate console.log('\n[STEP 2] Navigating to pokerogue.net...'); await page.goto('https://pokerogue.net/', { waitUntil: 'domcontentloaded', timeout: 60000 }); console.log(' Waiting 20s for Phaser...'); await sleep(20000); await shot(page, '01-initial-load'); // Step 3: Check messages console.log('\n[STEP 3] Extension messages check:'); for (const c of ['Game bridge loaded', 'SceneManager prototype patch installed', 'Game instance captured']) { console.log(` ${extMessages.some(m => m.includes(c)) ? '✓' : '✗'} ${c}`); } const gameCheck = await page.evaluate(() => ({ exists: !!window.__POKEXT_GAME__, hasScene: !!(window.__POKEXT_GAME__?.scene), hasCanvas: !!(window.__POKEXT_GAME__?.canvas), })); console.log(` __POKEXT_GAME__: ${JSON.stringify(gameCheck)}`); // Step 4: Login console.log('\n[STEP 4] Logging in...'); // Focus canvas and press Enter to get to login screen await page.mouse.click(640, 450); await sleep(1000); await page.keyboard.press('Enter'); await sleep(2000); await shot(page, '02-login-screen'); // Find visible inputs by coordinates const inputInfo = await page.evaluate(() => { const all = Array.from(document.querySelectorAll('input')); return all.map((inp, i) => { const r = inp.getBoundingClientRect(); const s = window.getComputedStyle(inp); return { i, type: inp.type, vis: r.width > 0 && r.height > 0 && s.display !== 'none' && s.visibility !== 'hidden', x: r.x, y: r.y, w: r.width, h: r.height, }; }).filter(x => x.vis); }); console.log(` Visible inputs: ${JSON.stringify(inputInfo)}`); const textInputs = inputInfo.filter(i => i.type === 'text'); const pwdInputs = inputInfo.filter(i => i.type === 'password'); if (textInputs.length > 0 && pwdInputs.length > 0) { const ui = textInputs[0]; const pi = pwdInputs[0]; // Click username field and type await page.mouse.click(ui.x + ui.w / 2, ui.y + ui.h / 2); await sleep(300); await page.keyboard.press('Control+a'); await sleep(100); await page.keyboard.type(USERNAME, { delay: 30 }); await sleep(300); // Click password field and type await page.mouse.click(pi.x + pi.w / 2, pi.y + pi.h / 2); await sleep(300); await page.keyboard.press('Control+a'); await sleep(100); await page.keyboard.type(PASSWORD, { delay: 30 }); await sleep(300); await shot(page, '03-login-filled'); // Verify const vals = await page.evaluate(({ uIdx, pIdx }) => { const inputs = document.querySelectorAll('input'); return { u: inputs[uIdx]?.value, pLen: inputs[pIdx]?.value?.length }; }, { uIdx: ui.i, pIdx: pi.i }); console.log(` Filled: user="${vals.u}", pwdLen=${vals.pLen}`); // Submit - press Enter await page.keyboard.press('Enter'); console.log(' Submitted login form'); await sleep(5000); await shot(page, '04-after-login'); // Check if we need to retry const loginResult = await page.evaluate(() => { const text = document.body.innerText || ''; return { hasError: text.includes('incorrect') || text.includes('must not be empty') || text.includes('Invalid'), snippet: text.substring(0, 200), }; }); if (loginResult.hasError) { console.log(` Login may have failed: ${loginResult.snippet.substring(0, 100)}`); // Try again - click username, clear, retype await page.mouse.click(ui.x + ui.w / 2, ui.y + ui.h / 2); await sleep(200); await page.keyboard.press('Control+a'); await page.keyboard.press('Backspace'); await sleep(100); await page.keyboard.type(USERNAME, { delay: 50 }); await sleep(200); await page.mouse.click(pi.x + pi.w / 2, pi.y + pi.h / 2); await sleep(200); await page.keyboard.press('Control+a'); await page.keyboard.press('Backspace'); await sleep(100); await page.keyboard.type(PASSWORD, { delay: 50 }); await sleep(200); // Now find and click the Login button in the canvas area // The button is below the password field typically // Try pressing Enter from password field await page.keyboard.press('Enter'); await sleep(5000); await shot(page, '05-login-retry'); } } else { console.log(' No visible login inputs found!'); } // Wait for login to complete and main menu to appear await sleep(3000); await shot(page, '06-post-login'); // Step 5: Start battle console.log('\n[STEP 5] Starting a battle...'); // Focus canvas await page.mouse.click(640, 450); await sleep(500); // Check current game phase/state const preMenuState = await page.evaluate(() => { const g = window.__POKEXT_GAME__; if (!g?.scene) return { noGame: true }; const scenes = g.scene.scenes || []; const battleScene = scenes.find(s => s.currentBattle !== undefined); return { sceneKeys: scenes.map(s => s.sys?.settings?.key).slice(0, 10), hasBattleScene: !!battleScene, currentPhase: battleScene?.currentPhase?.constructor?.name || 'none', currentBattle: battleScene?.currentBattle ? { waveIndex: battleScene.currentBattle.waveIndex, turn: battleScene.currentBattle.turn, } : null, }; }); console.log(` Pre-menu state: ${JSON.stringify(preMenuState)}`); // Navigate through menus // After login: main menu appears. Press Enter to select first option. // If "Continue" is available, it starts from last save. // If "New Game", it starts character selection. let battleFound = false; for (let attempt = 0; attempt < 25; attempt++) { const bc = await page.evaluate(() => { const g = window.__POKEXT_GAME__; if (!g?.scene) return { battle: false }; const scenes = g.scene.scenes || []; for (const s of scenes) { if (s.currentBattle) { const pf = typeof s.getPlayerField === 'function' ? s.getPlayerField() : []; const ef = typeof s.getEnemyField === 'function' ? s.getEnemyField() : []; if (pf.length > 0 && ef.length > 0) { return { battle: true, wave: s.currentBattle.waveIndex, players: pf.length, enemies: ef.length }; } return { battle: false, partial: true, wave: s.currentBattle.waveIndex }; } } return { battle: false }; }); if (bc.battle) { console.log(` Battle found at attempt ${attempt + 1}! Wave: ${bc.wave}, Players: ${bc.players}, Enemies: ${bc.enemies}`); battleFound = true; break; } if (attempt % 5 === 0 && attempt > 0) { console.log(` Attempt ${attempt}: no battle yet (${JSON.stringify(bc)})`); await shot(page, `07-nav-attempt-${attempt}`); } // Press Enter to advance through menus/dialogs await page.keyboard.press('Enter'); await sleep(1500); } if (!battleFound) { console.log(' Battle not found after Enter presses, trying arrow navigation...'); // Try selecting different menu options for (let i = 0; i < 3; i++) { await page.keyboard.press('ArrowDown'); await sleep(500); } await page.keyboard.press('Enter'); await sleep(3000); for (let attempt = 0; attempt < 10; attempt++) { const bc = await page.evaluate(() => { const g = window.__POKEXT_GAME__; if (!g?.scene) return { battle: false }; return { battle: (g.scene.scenes || []).some(s => s.currentBattle) }; }); if (bc.battle) { battleFound = true; console.log(` Battle found after arrow nav!`); break; } await page.keyboard.press('Enter'); await sleep(2000); } } // Wait for battle to fully load await sleep(5000); await shot(page, '08-battle-state'); // Step 6: Verify overlay console.log('\n[STEP 6] Verifying overlay...'); await sleep(3000); // Let polling catch up const finalState = await page.evaluate(() => { const g = window.__POKEXT_GAME__; const overlay = document.getElementById('poke-ext-overlay'); let battleInfo = null; if (g?.scene) { for (const s of (g.scene.scenes || [])) { if (s.currentBattle) { try { const pf = typeof s.getPlayerField === 'function' ? s.getPlayerField() : []; const ef = typeof s.getEnemyField === 'function' ? s.getEnemyField() : []; battleInfo = { wave: s.currentBattle.waveIndex, double: !!s.currentBattle.double, player: pf.filter(Boolean).map(p => ({ name: typeof p.getNameToRender === 'function' ? p.getNameToRender() : (p.name || '?'), types: typeof p.getTypes === 'function' ? p.getTypes() : [], moves: (p.moveset || []).filter(Boolean).map(m => { const mv = typeof m.getMove === 'function' ? m.getMove() : m; return { name: typeof m.getName === 'function' ? m.getName() : (mv?.name || '?'), type: mv?.type ?? -1, power: mv?.power ?? 0, cat: mv?.category ?? -1 }; }), })), enemy: ef.filter(Boolean).map(p => ({ name: typeof p.getNameToRender === 'function' ? p.getNameToRender() : (p.name || '?'), types: typeof p.getTypes === 'function' ? p.getTypes() : [], })), }; } catch (e) { battleInfo = { error: e.message }; } } } } return { gameExists: !!g, battleInfo, overlay: { exists: !!overlay, visible: overlay ? overlay.style.display !== 'none' : false, html: overlay ? overlay.innerHTML : '', text: overlay ? overlay.innerText : '', }, }; }); console.log(` Game exists: ${finalState.gameExists}`); console.log(` Battle: ${JSON.stringify(finalState.battleInfo, null, 2)}`); console.log(` Overlay exists: ${finalState.overlay.exists}, visible: ${finalState.overlay.visible}`); console.log(` Overlay text:\n${finalState.overlay.text}`); console.log(` Overlay HTML (1500 chars):\n${finalState.overlay.html.substring(0, 1500)}`); await shot(page, '09-final-with-overlay'); // Try to get overlay closeup if (finalState.overlay.exists) { try { const el = await page.$('#poke-ext-overlay'); if (el) { await el.screenshot({ path: path.join(SCREENSHOT_DIR, 'test-10-overlay-closeup.png') }); console.log('[SHOT] test-10-overlay-closeup.png'); } } catch (_) {} } // Step 7: All ext messages console.log('\n[STEP 7] All [PokeRogue Ext] messages:'); console.log('─'.repeat(60)); extMessages.forEach((m, i) => console.log(` ${i + 1}. ${m}`)); if (!extMessages.length) console.log(' (none)'); console.log('─'.repeat(60)); const errs = consoleMessages.filter(m => m.type === 'error'); if (errs.length) { console.log(`\n Errors (${errs.length}):`); errs.slice(0, 10).forEach(e => console.log(` ${e.text.substring(0, 200)}`)); } console.log('\n[SUMMARY]'); console.log(` ${gameCheck.exists ? '✓' : '✗'} Game captured via SceneManager patch`); console.log(` ${finalState.overlay.exists ? '✓' : '✗'} Overlay created`); console.log(` ${finalState.battleInfo ? '✓' : '✗'} Battle detected`); if (finalState.battleInfo && !finalState.battleInfo.error) { console.log(` ${finalState.overlay.text.includes('Effectiveness') ? '✓' : '✗'} Overlay shows type effectiveness`); } console.log(` Extension messages: ${extMessages.length}`); await context.close(); try { fs.rmSync(USER_DATA_DIR, { recursive: true, force: true }); } catch (_) {} console.log('\n[TEST] Done!'); } main().catch(err => { console.error('[FATAL]', err.message); process.exit(1); });