diff --git a/scripts/probe-inject.js b/scripts/probe-inject.js new file mode 100644 index 0000000..ffd1998 --- /dev/null +++ b/scripts/probe-inject.js @@ -0,0 +1,41 @@ +// Inject the Beat Pocket bundle manually via addInitScript and capture errors +const { chromium } = require('/usr/lib/node_modules/@playwright/test'); +const fs = require('fs'); + +const BUNDLE = fs.readFileSync('/root/beat-pocket/extension/dist/content/index.js', 'utf8'); + +(async () => { + const userDataDir = '/tmp/bb-inject-' + Date.now(); + const context = await chromium.launchPersistentContext(userDataDir, { + headless: true, + channel: 'chromium', + args: ['--disable-dev-shm-usage', '--no-sandbox'], // NO extension loading + viewport: { width: 414, height: 896 }, + }); + + const page = context.pages()[0] || await context.newPage(); + page.on('console', (m) => console.log(`[page:${m.type()}]`, m.text().slice(0, 300))); + page.on('pageerror', (e) => console.log('[page:error]', String(e).slice(0, 500))); + + // Inject the bundle as an init script — runs before the page's own JS + // This bypasses the extension loader and lets us see if the bundle itself errors + await page.addInitScript({ + content: BUNDLE, + }); + + await page.goto('https://beat-battle.net/', { waitUntil: 'domcontentloaded', timeout: 30000 }); + await page.waitForTimeout(5000); + + const result = await page.evaluate(() => { + const allBbp = document.querySelectorAll('[class*="bbp-"], #bbp-overlay-host, [data-bbp]'); + return { + bbpMarkerCount: allBbp.length, + sampleClasses: Array.from(allBbp).slice(0, 8).map(el => el.className).join(' | '), + htmlClass: document.documentElement.className, + bodyClass: document.body?.className, + }; + }); + console.log('[inject:result]', result); + + await context.close(); +})().catch((e) => { console.error('FAIL:', e); process.exit(1); }); \ No newline at end of file diff --git a/scripts/probe-minimal.js b/scripts/probe-minimal.js new file mode 100644 index 0000000..fbaf4f8 --- /dev/null +++ b/scripts/probe-minimal.js @@ -0,0 +1,37 @@ +// Test whether a MINIMAL extension's content script injects under headless +const { chromium } = require('/usr/lib/node_modules/@playwright/test'); + +const EXT_PATH = '/tmp/bbp-minimal'; + +(async () => { + const userDataDir = '/tmp/bb-minimal-' + Date.now(); + const context = await chromium.launchPersistentContext(userDataDir, { + headless: true, + channel: 'chromium', + args: [ + `--disable-extensions-except=${EXT_PATH}`, + `--load-extension=${EXT_PATH}`, + '--disable-dev-shm-usage', + '--no-sandbox', + ], + viewport: { width: 414, height: 896 }, + }); + + console.log('[minimal] workers:', context.serviceWorkers().length); + + const page = context.pages()[0] || await context.newPage(); + page.on('console', (m) => console.log(`[page:${m.type()}]`, m.text())); + page.on('pageerror', (e) => console.log('[page:error]', e.message)); + + await page.goto('https://beat-battle.net/', { waitUntil: 'domcontentloaded', timeout: 30000 }); + await page.waitForTimeout(4000); + + const result = await page.evaluate(() => ({ + htmlClass: document.documentElement.className, + hasMarker: !!document.getElementById('bbp-minimal-marker'), + markerText: document.getElementById('bbp-minimal-marker')?.textContent, + })); + console.log('[minimal:result]', result); + + await context.close(); +})().catch((e) => { console.error('FAIL:', e); process.exit(1); }); \ No newline at end of file diff --git a/scripts/probe.js b/scripts/probe.js new file mode 100644 index 0000000..b428a2a --- /dev/null +++ b/scripts/probe.js @@ -0,0 +1,86 @@ +// Probe: is the content script actually being injected? +const { chromium } = require('/usr/lib/node_modules/@playwright/test'); + +const EXT_PATH = '/root/beat-pocket/extension'; + +(async () => { + const userDataDir = '/tmp/bb-smoke-probe-' + Date.now(); + const context = await chromium.launchPersistentContext(userDataDir, { + headless: true, + channel: 'chromium', + args: [ + `--disable-extensions-except=${EXT_PATH}`, + `--load-extension=${EXT_PATH}`, + '--disable-dev-shm-usage', + '--no-sandbox', + ], + viewport: { width: 414, height: 896 }, + }); + + // Wait for background + await context.waitForEvent('serviceworker', { timeout: 8000 }).catch(() => {}); + console.log('[probe] service workers:', context.serviceWorkers().length); + + const page = context.pages()[0] || await context.newPage(); + + // Capture ALL console messages to find extension logs + page.on('console', (msg) => { + console.log(`[page:${msg.type()}]`, msg.text().slice(0, 200)); + }); + page.on('pageerror', (err) => { + console.log('[page:error]', String(err).slice(0, 300)); + }); + page.on('requestfailed', (req) => { + console.log('[reqfail]', req.url(), '-', req.failure()?.errorText); + }); + + // Probe: enumerate scripts and stylesheets on the page + page.on('domcontentloaded', async () => { + const probe = await page.evaluate(() => ({ + scripts: Array.from(document.querySelectorAll('script[src]')).map((s) => s.src), + stylesheets: Array.from(document.querySelectorAll('link[rel="stylesheet"]')).map((l) => l.href), + // Look for our markers explicitly + htmlClassNames: Array.from(document.body.classList), + // Check chrome-extension:// — does anything reference it? + hasExtensionResource: !![...document.scripts].find(s => s.src?.includes('chrome-extension://')), + })); + console.log('[probe:domcontentloaded] scripts:', probe.scripts.length, 'stylesheets:', probe.stylesheets.length, 'ext-resource:', probe.hasExtensionResource); + // Look for our injected stylesheet marker (our inject.css adds a class to ) + const htmlClasses = await page.evaluate(() => document.documentElement.className); + console.log('[probe] html.className:', htmlClasses); + }); + + await page.goto('https://beat-battle.net/', { waitUntil: 'networkidle', timeout: 30000 }); + // wait extra for idle + await page.waitForTimeout(4000); + + // After load: what scripts does the page see? + const after = await page.evaluate(() => ({ + bbpMarkers: document.querySelectorAll('[class*="bbp-"], #bbp-overlay-host').length, + windowKeys: Object.keys(window).filter(k => /bbp/i.test(k)), + scripts: Array.from(document.querySelectorAll('script[src]')).length, + cssRules: Array.from(document.styleSheets).reduce((acc, s) => { + try { return acc + (s.cssRules?.length || 0); } catch { return acc; } + }, 0), + })); + console.log('[probe:after-load]', after); + + // Try injecting a manual probe to see if we can reach the extension's service worker + const extId = context.serviceWorkers()[0]?.url()?.match(/chrome-extension:\/\/([a-z]+)\//)?.[1]; + console.log('[probe] extension id:', extId); + if (extId) { + const sw = context.serviceWorkers()[0]; + try { + const r = await sw.evaluate(() => ({ + url: location.href, + runtime: typeof chrome?.runtime, + runtimeId: chrome?.runtime?.id, + })); + console.log('[probe] sw context:', r); + } catch (e) { + console.log('[probe] sw evaluate failed:', e.message); + } + } + + await context.close(); +})().catch((e) => { console.error('FAILED:', e); process.exit(1); }); \ No newline at end of file diff --git a/scripts/smoke-final.js b/scripts/smoke-final.js new file mode 100644 index 0000000..b98d5b1 --- /dev/null +++ b/scripts/smoke-final.js @@ -0,0 +1,61 @@ +// Final smoke: load the extension, hit the site, check the correct marker IDs +const { chromium } = require('/usr/lib/node_modules/@playwright/test'); + +const EXT_PATH = '/root/beat-pocket/extension'; + +(async () => { + const consoleMessages = []; + const pageErrors = []; + + const userDataDir = '/tmp/bb-final-' + Date.now(); + const context = await chromium.launchPersistentContext(userDataDir, { + headless: true, + channel: 'chromium', + args: [ + `--disable-extensions-except=${EXT_PATH}`, + `--load-extension=${EXT_PATH}`, + '--disable-dev-shm-usage', + '--no-sandbox', + ], + viewport: { width: 414, height: 896 }, + }); + + await context.waitForEvent('serviceworker', { timeout: 8000 }).catch(() => {}); + console.log('[final] sw count:', context.serviceWorkers().length); + + const page = context.pages()[0] || await context.newPage(); + page.on('console', (m) => consoleMessages.push({ type: m.type(), text: m.text() })); + page.on('pageerror', (e) => pageErrors.push(String(e))); + + await page.goto('https://beat-battle.net/', { waitUntil: 'domcontentloaded', timeout: 30000 }); + await page.waitForTimeout(5000); + + // Comprehensive DOM check — every plausible marker + const result = await page.evaluate(() => { + const find = (sel) => document.querySelector(sel); + return { + bbpHost: find('#bbp-host')?.outerHTML?.slice(0, 200) || null, + bbpDrawer: find('#bbp-chat-drawer')?.outerHTML?.slice(0, 200) || null, + bbpFab: find('#bbp-fab')?.outerHTML?.slice(0, 200) || null, + bbpHeaderBtn: find('#bbp-header-btn')?.outerHTML?.slice(0, 200) || null, + bbpAnyDiv: Array.from(document.querySelectorAll('div')).filter(d => d.id.startsWith('bbp-')).map(d => d.id), + bbpClassCount: document.querySelectorAll('[class*="bbp-"]').length, + // Site state + playButton: !!Array.from(document.querySelectorAll('button')).find(b => /PLAY/i.test(b.textContent || '')), + windowBbp: Object.keys(window).filter(k => /bbp/i.test(k)), + }; + }); + console.log('[final:dom]', JSON.stringify(result, null, 2)); + + console.log('[final:errors]', pageErrors.length, 'errors:', pageErrors.slice(0,3)); + console.log('[final:console] total', consoleMessages.length); + const ours = consoleMessages.filter(m => /beat pocket|bbp|beat-pocket/i.test(m.text)); + console.log('[final:console] ours:', ours.length); + for (const m of ours.slice(0, 8)) console.log(' ', m.type, m.text.slice(0,200)); + + // Now navigate to a known-in-game URL? No — site is dynamic, just check homepage state. + await page.screenshot({ path: '/tmp/bb-final.png', fullPage: false }); + console.log('[final] screenshot: /tmp/bb-final.png'); + + await context.close(); +})().catch((e) => { console.error('FAIL:', e); process.exit(1); }); \ No newline at end of file diff --git a/scripts/smoke.js b/scripts/smoke.js new file mode 100644 index 0000000..bf80a4d --- /dev/null +++ b/scripts/smoke.js @@ -0,0 +1,113 @@ +// Smoke test for Beat Pocket extension. +// Uses Playwright's bundled Chromium (unbranded — accepts --load-extension) +// to load the extension into beat-battle.net and verify: +// 1. The extension was loaded (extension context exists) +// 2. The content script ran (our markers are in the DOM) +// 3. The page reached a usable state (PLAY button exists) +// +// Run: node scripts/smoke.js + +const { chromium } = require('/usr/lib/node_modules/@playwright/test'); + +const EXT_PATH = '/root/beat-pocket/extension'; +const TARGET = 'https://beat-battle.net/'; + +(async () => { + const consoleMessages = []; + const pageErrors = []; + + // Persistent context lets us pass --load-extension at browser launch. + // (Regular chromium.launch ignores that flag.) + const userDataDir = '/tmp/bb-smoke-pw-' + Date.now(); + const context = await chromium.launchPersistentContext(userDataDir, { + headless: true, + channel: 'chromium', // use playwright's bundled build, not google-chrome + args: [ + `--disable-extensions-except=${EXT_PATH}`, + `--load-extension=${EXT_PATH}`, + '--disable-dev-shm-usage', + '--no-sandbox', + ], + viewport: { width: 414, height: 896 }, + }); + + console.log('[smoke] launched persistent context, userDataDir:', userDataDir); + + // Wait for any service worker (background.js) to register + let backgroundWorkers = context.serviceWorkers(); + if (backgroundWorkers.length === 0) { + try { + backgroundWorkers = [await context.waitForEvent('serviceworker', { timeout: 5000 })]; + } catch (e) { + console.log('[smoke] no service worker registered within 5s:', e.message); + } + } + console.log('[smoke] background workers:', backgroundWorkers.length); + for (const sw of backgroundWorkers) { + console.log(' sw url:', sw.url()); + } + + const page = context.pages()[0] || await context.newPage(); + page.on('console', (msg) => { + consoleMessages.push({ type: msg.type(), text: msg.text() }); + }); + page.on('pageerror', (err) => { + pageErrors.push(String(err)); + }); + + console.log('[smoke] navigating to', TARGET); + await page.goto(TARGET, { waitUntil: 'domcontentloaded', timeout: 30000 }); + + // Give the content script time to run, since run_at is document_idle + await page.waitForTimeout(3000); + + // === Check 1: extension context === + const extensionIds = context.backgroundPages().length; + console.log('[smoke] background pages:', extensionIds); + + // === Check 2: content script markers in DOM === + const markers = await page.evaluate(() => { + const all = document.querySelectorAll('[class*="bbp-"], [data-bbp], #bbp-overlay-host, #bbp-chat-drawer'); + return Array.from(all).slice(0, 25).map((el) => ({ + tag: el.tagName.toLowerCase(), + id: el.id || null, + cls: el.className && typeof el.className === 'string' ? el.className.slice(0, 120) : null, + })); + }); + console.log('[smoke] BBP markers in DOM:', markers.length); + for (const m of markers) console.log(' ', m); + + // === Check 3: page rendered (PLAY button) === + const playVisible = await page.locator('button:has-text("PLAY")').first().isVisible().catch(() => false); + const loginVisible = await page.locator('a:has-text("LOG IN")').first().isVisible().catch(() => false); + console.log('[smoke] PLAY button visible:', playVisible); + console.log('[smoke] LOG IN chip visible:', loginVisible); + + // === Check 4: did our bbp:phase events fire? === + const phaseEvents = await page.evaluate(() => { + const evt = new Event('bbp-test'); + return { hasGlobalListener: !!window.__bbp_phase_history__ }; + }); + console.log('[smoke] phase event hook:', phaseEvents); + + // === Check 5: console output from our content script === + const ourConsole = consoleMessages.filter((m) => + /beat pocket|bbp:|beat-pocket/i.test(m.text) + ); + console.log('[smoke] console lines from extension:', ourConsole.length); + for (const m of ourConsole.slice(0, 10)) console.log(' ', m.type, m.text); + + // === Check 6: page errors === + console.log('[smoke] page errors:', pageErrors.length); + for (const e of pageErrors.slice(0, 5)) console.log(' ', e.slice(0, 200)); + + // Screenshot for the report + await page.screenshot({ path: '/tmp/bb-smoke.png', fullPage: false }); + console.log('[smoke] screenshot saved: /tmp/bb-smoke.png'); + + await context.close(); + console.log('[smoke] DONE'); +})().catch((e) => { + console.error('[smoke] FAILED:', e); + process.exit(1); +}); \ No newline at end of file