Files
Zebratic 78ebc4fffd Add Playwright-based smoke test scripts
scripts/smoke.js, scripts/probe.js, scripts/probe-inject.js,
scripts/probe-minimal.js, scripts/smoke-final.js

The smoke test verifies the Beat Pocket extension loads and injects into
beat-battle.net:

  1. Service worker registers (manifest valid, MV3 wiring works)
  2. Content script runs (creates #bbp-host element on document.body)
  3. No JS errors thrown on the page
  4. Site renders normally (PLAY button still visible)

Why Playwright instead of raw --load-extension:
  - Google Chrome refuses --load-extension on the command line
  - Chromium full build accepts it but hangs under --virtual-time-budget
  - Playwright's launchPersistentContext properly initializes extensions
    in headless mode and gives a clean Chromium without the GCM/dbus noise
  - The bundled Chromium is at
    /root/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome

Run with: node scripts/smoke-final.js (after npm install)

Probe results from this session:
  - [final:dom] #bbp-host is appended to body via ensureHost()
  - [final:errors] 0 page errors
  - [final:console] 0 extension-related console lines (expected: silent
    happy path; logs only on phase transitions)
2026-07-11 02:40:06 +00:00

86 lines
3.5 KiB
JavaScript

// 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 <html>)
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); });