Auto-detect Chrome for Testing to prevent version mismatch

When no chrome_binary is configured, auto-detect Chrome for Testing
at ~/.chrome-for-testing/ and use it instead of letting ChromeDriver
fall back to system Chrome. This prevents the frequent version
mismatch error caused by system Chrome auto-updating independently
of the ChromeDriver installed by setup-chrome-for-testing.sh.

Checks mac-arm64, mac-x64, and linux64 paths. Falls back to system
Chrome (previous behavior) if Chrome for Testing is not installed.
This commit is contained in:
Dhanji R. Prasanna
2026-02-14 14:56:33 +11:00
parent 92352e1897
commit 22b1ab93e4

View File

@@ -11,6 +11,38 @@ use crate::ToolCall;
use super::executor::ToolContext;
// ─────────────────────────────────────────────────────────────────────────────
// Chrome for Testing auto-detection
// ─────────────────────────────────────────────────────────────────────────────
/// Auto-detect Chrome for Testing binary at ~/.chrome-for-testing/.
///
/// When no `chrome_binary` is configured, this checks for a Chrome for Testing
/// installation which is version-locked to the matching ChromeDriver, avoiding
/// version mismatch errors caused by system Chrome auto-updating.
fn detect_chrome_for_testing() -> Option<String> {
let home_str = std::env::var("HOME").ok()?;
let home = std::path::PathBuf::from(home_str);
let cft_dir = home.join(".chrome-for-testing");
// Check platform-specific directories
let candidates = [
cft_dir.join("chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing"),
cft_dir.join("chrome-mac-x64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing"),
// Linux paths
cft_dir.join("chrome-linux64/chrome"),
];
for candidate in &candidates {
if candidate.exists() {
debug!("Auto-detected Chrome for Testing: {}", candidate.display());
return Some(candidate.to_string_lossy().into_owned());
}
}
None
}
// ─────────────────────────────────────────────────────────────────────────────
// Port checking helpers
// ─────────────────────────────────────────────────────────────────────────────
@@ -138,12 +170,17 @@ async fn start_safari_driver<W: UiWriter>(ctx: &ToolContext<'_, W>) -> Result<St
async fn start_chrome_driver<W: UiWriter>(ctx: &ToolContext<'_, W>) -> Result<String> {
let port = ctx.config.webdriver.chrome_port;
// Resolve Chrome binary: use configured path, or auto-detect Chrome for Testing
let chrome_binary = ctx.config.webdriver.chrome_binary.clone()
.or_else(|| detect_chrome_for_testing());
let chrome_binary_ref = chrome_binary.as_deref();
// Check if chromedriver is already running on this port
let already_running = check_chromedriver_running(port).await;
if already_running {
// Try to connect to existing chromedriver
let driver_result = match &ctx.config.webdriver.chrome_binary {
let driver_result = match chrome_binary_ref {
Some(binary) => {
g3_computer_control::ChromeDriver::with_port_headless_and_binary(port, Some(binary))
.await
@@ -203,7 +240,7 @@ async fn start_chrome_driver<W: UiWriter>(ctx: &ToolContext<'_, W>) -> Result<St
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
// Try to connect to ChromeDriver in headless mode (with optional custom binary)
let driver_result = match &ctx.config.webdriver.chrome_binary {
let driver_result = match chrome_binary_ref {
Some(binary) => {
g3_computer_control::ChromeDriver::with_port_headless_and_binary(port, Some(binary))
.await