@@ -58,9 +58,13 @@ async fn launch_stealth_browser(
5858 . build ( )
5959 . map_err ( |e| format ! ( "browser config: {e}" ) ) ?;
6060
61- let ( browser, mut handler) = Browser :: launch ( config)
62- . await
63- . map_err ( |e| format ! ( "browser launch: {e}" ) ) ?;
61+ let ( browser, mut handler) = tokio:: time:: timeout (
62+ Duration :: from_secs ( 30 ) ,
63+ Browser :: launch ( config) ,
64+ )
65+ . await
66+ . map_err ( |_| "browser launch timed out" . to_string ( ) ) ?
67+ . map_err ( |e| format ! ( "browser launch: {e}" ) ) ?;
6468
6569 let handle = tokio:: spawn ( async move {
6670 while let Some ( event) = handler. next ( ) . await {
@@ -336,19 +340,23 @@ async fn test_stealth_sannysoft() {
336340 tokio:: time:: sleep ( Duration :: from_secs ( 5 ) ) . await ;
337341
338342 // Extract all test results from the table rows.
343+ // Sannysoft uses multiple tables — grab all rows from any table on the page.
339344 let results: serde_json:: Value = page
340345 . evaluate ( r#"
341346 (() => {
342- const rows = document.querySelectorAll('table#advanced-table tr, table#basic-table tr');
347+ const rows = document.querySelectorAll('table tr');
343348 const results = {};
344349 rows.forEach(row => {
345350 const cells = row.querySelectorAll('td');
346351 if (cells.length >= 2) {
347352 const name = cells[0].textContent.trim();
348353 const value = cells[1].textContent.trim();
354+ const style = window.getComputedStyle(cells[1]);
349355 const failed = cells[1].classList.contains('failed') ||
350- cells[1].style.backgroundColor === 'red' ||
351- cells[1].style.background === 'red';
356+ style.backgroundColor.includes('255, 0, 0') ||
357+ style.backgroundColor.includes('rgb(255') ||
358+ value.toLowerCase() === 'headless' ||
359+ value.toLowerCase() === 'true';
352360 results[name] = { value, failed };
353361 }
354362 });
@@ -403,9 +411,20 @@ async fn test_stealth_infosimples() {
403411 . await
404412 . expect ( "inject stealth" ) ;
405413
406- page. goto ( "https://infosimples.github.io/detect-headless" )
407- . await
408- . expect ( "navigate" ) ;
414+ let nav_result = tokio:: time:: timeout (
415+ Duration :: from_secs ( 20 ) ,
416+ page. goto ( "https://infosimples.github.io/detect-headless" ) ,
417+ )
418+ . await ;
419+
420+ eprintln ! ( "\n === INFOSIMPLES RESULTS ===" ) ;
421+ if nav_result. is_err ( ) || nav_result. unwrap ( ) . is_err ( ) {
422+ eprintln ! ( " SKIPPED — site timed out or unreachable" ) ;
423+ let _ = browser. close ( ) . await ;
424+ handle. abort ( ) ;
425+ return ; // Don't fail the test for site unavailability.
426+ }
427+
409428 tokio:: time:: sleep ( Duration :: from_secs ( 5 ) ) . await ;
410429
411430 let results: serde_json:: Value = page
@@ -434,7 +453,6 @@ async fn test_stealth_infosimples() {
434453 . into_value ( )
435454 . expect ( "parse results" ) ;
436455
437- eprintln ! ( "\n === INFOSIMPLES RESULTS ===" ) ;
438456 if let Some ( obj) = results. as_object ( ) {
439457 let mut headless_count = 0 ;
440458 for ( test, result) in obj {
@@ -447,7 +465,6 @@ async fn test_stealth_infosimples() {
447465 }
448466 }
449467 eprintln ! ( " Total: {} tests, {} detected as headless" , obj. len( ) , headless_count) ;
450- // Allow some detections (alert timing, mouse move) but critical ones must pass.
451468 for critical in & [ "Webdriver" , "Chrome element" , "Plugins" , "Languages" , "Permission" ] {
452469 if let Some ( r) = obj. get ( * critical) {
453470 let headless = r. get ( "headless" ) . and_then ( |v| v. as_bool ( ) ) . unwrap_or ( false ) ;
0 commit comments