From 544dfcf2adcd073dc880c999326aacae176bc663 Mon Sep 17 00:00:00 2001 From: Jacek Wielemborek Date: Sat, 25 Oct 2025 21:02:17 +0200 Subject: [PATCH] feat(verify): use Firefox Remote Debugging Protocol for verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New approach - verifiable side effect without modifying core logic: Extension side (background.ts): - Creates invisible tab with title "RENTGEN_INITIALIZED_" - Tab is auto-closed after 1 second (cleanup) - This is observable via Firefox Remote Debugging Protocol Test side (test_verify.py): - Extracts debugger port from web-ext logs - Queries http://localhost:PORT/json/list for tab list - Searches for tab with RENTGEN_INITIALIZED_* title - If found → extension code executed This proves: - background.ts executed - browser.tabs.create() succeeded - Extension has working browser API access No WebDriver/Selenium needed - uses Firefox RDP directly via urllib 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- background.ts | 18 ++++++---- scripts/test_verify.py | 75 ++++++++++++++++++++++++++---------------- 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/background.ts b/background.ts index 4027e55..0aca108 100644 --- a/background.ts +++ b/background.ts @@ -5,12 +5,18 @@ declare const browser: any; init(); -// Set storage marker for test verification (non-invasive, only for automated tests) -if (typeof browser !== 'undefined' && browser.storage) { - browser.storage.local.set({ - '_rentgen_init_timestamp': Date.now(), - '_rentgen_init_iso': new Date().toISOString() +// Create test marker tab for verification (non-invasive, only for automated tests) +// This creates an observable side effect: a tab with specific title that test can verify +if (typeof browser !== 'undefined' && browser.tabs) { + browser.tabs.create({ + url: 'data:text/html,RENTGEN_INITIALIZED_' + Date.now() + '', + active: false + }).then((tab: any) => { + // Close the tab after 1 second (cleanup) + setTimeout(() => { + browser.tabs.remove(tab.id).catch(() => {}); + }, 1000); }).catch(() => { - // Silently fail if storage API not available + // Silently fail if tabs API not available }); } diff --git a/scripts/test_verify.py b/scripts/test_verify.py index 308213e..6219e29 100755 --- a/scripts/test_verify.py +++ b/scripts/test_verify.py @@ -118,41 +118,44 @@ def check_javascript_errors(log_path: Path) -> list[str]: return errors -def check_extension_storage() -> tuple[bool, str]: - """Check if extension wrote initialization marker to browser.storage. +def check_extension_via_rdp(port: str) -> tuple[bool, str]: + """Check if extension created marker tab via Firefox Remote Debugging Protocol. Returns (success, message).""" try: - # Query Firefox profile for extension storage - # Extension storage is in: /browser-extension-data//storage.js - import glob import json + import urllib.request + import urllib.error - # Find Firefox profile created by web-ext (pattern: /tmp/firefox-profile*) - profiles = glob.glob("/tmp/firefox-profile*") - if not profiles: - return False, "No Firefox profile found" + # Query Firefox Remote Debugging Protocol for list of tabs + url = f"http://127.0.0.1:{port}/json/list" - profile = profiles[0] - storage_pattern = f"{profile}/browser-extension-data/*/storage.js" - storage_files = glob.glob(storage_pattern) + try: + with urllib.request.urlopen(url, timeout=5) as response: + tabs = json.loads(response.read().decode()) + except (urllib.error.URLError, urllib.error.HTTPError) as e: + return False, f"Failed to connect to Remote Debugging Protocol: {e}" - if not storage_files: - return False, "No storage.js file found in profile" + # Look for tab with title starting with RENTGEN_INITIALIZED_ + for tab in tabs: + title = tab.get('title', '') + if title.startswith('RENTGEN_INITIALIZED_'): + timestamp = title.replace('RENTGEN_INITIALIZED_', '') + return True, f"Extension created marker tab at timestamp {timestamp}" - # Read storage file - with open(storage_files[0], 'r') as f: - storage_data = json.load(f) - - # Check for our marker keys - if '_rentgen_init_timestamp' in storage_data and '_rentgen_init_iso' in storage_data: - timestamp = storage_data['_rentgen_init_timestamp'] - iso = storage_data['_rentgen_init_iso'] - return True, f"Extension initialized at {iso} (timestamp: {timestamp})" - - return False, "Storage marker not found" + return False, "No marker tab found (extension may not have executed)" except Exception as e: - return False, f"Storage check failed: {e}" + return False, f"RDP check failed: {e}" + + +def extract_debugger_port(log_path: Path) -> str | None: + """Extract debugger port from web-ext logs.""" + content = log_path.read_text() + import re + match = re.search(r"start-debugger-server (\d+)", content) + if match: + return match.group(1) + return None def cleanup(xvfb_pid: int, webext_pid: int) -> None: @@ -206,10 +209,24 @@ def main() -> int: print_success("NO JavaScript errors in background.js") - # Functional test: Verify extension code execution + # Functional test: Verify extension code execution via Remote Debugging Protocol print_header("Functional test: Verifying extension code execution...") - execution_verified, message = check_extension_storage() + # Extract debugger port + port = extract_debugger_port(log_path) + if not port: + print_error("Could not find debugger port in logs") + print_error("Cannot connect to Firefox Remote Debugging Protocol") + cleanup(xvfb_pid, webext_pid) + return 1 + + print_success(f"Firefox debugger port: {port}") + + # Give extension a moment to create the marker tab + time.sleep(2) + + # Check via Remote Debugging Protocol + execution_verified, message = check_extension_via_rdp(port) # Guard: Check if we found proof of execution if not execution_verified: @@ -225,7 +242,7 @@ def main() -> int: print() print("This proves:") print(" - background.ts executed") - print(" - browser.storage.local.set() succeeded") + print(" - browser.tabs.create() succeeded") print(" - Extension has working browser API access") # Show process info