@@ -59,6 +59,13 @@ function expectReadyChromeCdpDiagnostic(
5959 return diagnostic ;
6060}
6161
62+ function jsonResponse ( payload : unknown , status = 200 ) : Response {
63+ return new Response ( JSON . stringify ( payload ) , {
64+ status,
65+ headers : { "content-type" : "application/json" } ,
66+ } ) ;
67+ }
68+
6269async function readJson ( filePath : string ) : Promise < Record < string , unknown > > {
6370 const raw = await fsp . readFile ( filePath , "utf-8" ) ;
6471 return JSON . parse ( raw ) as Record < string , unknown > ;
@@ -467,49 +474,47 @@ describe("browser chrome helpers", () => {
467474 it ( "reports reachability based on /json/version" , async ( ) => {
468475 vi . stubGlobal (
469476 "fetch" ,
470- vi . fn ( ) . mockResolvedValue ( {
471- ok : true ,
472- json : async ( ) => ( { webSocketDebuggerUrl : "ws://127.0.0.1/devtools" } ) ,
473- } as unknown as Response ) ,
477+ vi . fn ( ) . mockResolvedValue ( jsonResponse ( { webSocketDebuggerUrl : "ws://127.0.0.1/devtools" } ) ) ,
474478 ) ;
475479 await expect ( isChromeReachable ( "http://127.0.0.1:12345" , 50 ) ) . resolves . toBe ( true ) ;
476480
477- vi . stubGlobal (
478- "fetch" ,
479- vi . fn ( ) . mockResolvedValue ( {
480- ok : false ,
481- json : async ( ) => ( { } ) ,
482- } as unknown as Response ) ,
483- ) ;
481+ vi . stubGlobal ( "fetch" , vi . fn ( ) . mockResolvedValue ( jsonResponse ( { } , 500 ) ) ) ;
484482 await expect ( isChromeReachable ( "http://127.0.0.1:12345" , 50 ) ) . resolves . toBe ( false ) ;
485483
486484 vi . stubGlobal ( "fetch" , vi . fn ( ) . mockRejectedValue ( new Error ( "boom" ) ) ) ;
487485 await expect ( isChromeReachable ( "http://127.0.0.1:12345" , 50 ) ) . resolves . toBe ( false ) ;
488486 } ) ;
489487
490488 it ( "diagnoses /json/version responses that omit the websocket URL" , async ( ) => {
489+ vi . stubGlobal ( "fetch" , vi . fn ( ) . mockResolvedValue ( jsonResponse ( { Browser : "Chrome/Mock" } ) ) ) ;
490+
491+ const diagnostic = expectFailedChromeCdpDiagnostic (
492+ await diagnoseChromeCdp ( "http://127.0.0.1:12345" , 50 , 50 ) ,
493+ ) ;
494+ expect ( diagnostic . code ) . toBe ( "missing_websocket_debugger_url" ) ;
495+ expect ( diagnostic . cdpUrl ) . toBe ( "http://127.0.0.1:12345" ) ;
496+ } ) ;
497+
498+ it ( "preserves invalid-json diagnostics for bounded /json/version reads" , async ( ) => {
491499 vi . stubGlobal (
492500 "fetch" ,
493- vi . fn ( ) . mockResolvedValue ( {
494- ok : true ,
495- json : async ( ) => ( { Browser : "Chrome/Mock" } ) ,
496- } as unknown as Response ) ,
501+ vi . fn ( ) . mockResolvedValue (
502+ new Response ( "{" , {
503+ headers : { "content-type" : "application/json" } ,
504+ } ) ,
505+ ) ,
497506 ) ;
498507
499508 const diagnostic = expectFailedChromeCdpDiagnostic (
500509 await diagnoseChromeCdp ( "http://127.0.0.1:12345" , 50 , 50 ) ,
501510 ) ;
502- expect ( diagnostic . code ) . toBe ( "missing_websocket_debugger_url" ) ;
503- expect ( diagnostic . cdpUrl ) . toBe ( "http://127.0.0.1:12345" ) ;
511+ expect ( diagnostic . code ) . toBe ( "invalid_json" ) ;
504512 } ) ;
505513
506514 it ( "allows loopback CDP probes while still blocking non-loopback private targets in strict SSRF mode" , async ( ) => {
507515 const fetchSpy = vi
508516 . fn ( )
509- . mockResolvedValueOnce ( {
510- ok : true ,
511- json : async ( ) => ( { webSocketDebuggerUrl : "ws://127.0.0.1/devtools" } ) ,
512- } as unknown as Response )
517+ . mockResolvedValueOnce ( jsonResponse ( { webSocketDebuggerUrl : "ws://127.0.0.1/devtools" } ) )
513518 . mockRejectedValue ( new Error ( "should not be called" ) ) ;
514519 vi . stubGlobal ( "fetch" , fetchSpy ) ;
515520
@@ -794,10 +799,7 @@ describe("browser chrome helpers", () => {
794799 // connections (Browserless/Browserbase-style provider).
795800 vi . stubGlobal (
796801 "fetch" ,
797- vi . fn ( ) . mockResolvedValue ( {
798- ok : true ,
799- json : async ( ) => ( { } ) , // empty — no webSocketDebuggerUrl
800- } as unknown as Response ) ,
802+ vi . fn ( ) . mockResolvedValue ( jsonResponse ( { } ) ) , // empty — no webSocketDebuggerUrl
801803 ) ;
802804 // A real WS server accepts the handshake.
803805 const wss = new WebSocketServer ( {
@@ -819,13 +821,7 @@ describe("browser chrome helpers", () => {
819821 } ) ;
820822
821823 it ( "falls back to a direct WS readiness check when /json/version has no debugger URL" , async ( ) => {
822- vi . stubGlobal (
823- "fetch" ,
824- vi . fn ( ) . mockResolvedValue ( {
825- ok : true ,
826- json : async ( ) => ( { } ) ,
827- } as unknown as Response ) ,
828- ) ;
824+ vi . stubGlobal ( "fetch" , vi . fn ( ) . mockResolvedValue ( jsonResponse ( { } ) ) ) ;
829825 const wss = new WebSocketServer ( {
830826 port : 0 ,
831827 host : "127.0.0.1" ,
@@ -859,13 +855,7 @@ describe("browser chrome helpers", () => {
859855 it ( "returns the original ws:// URL from getChromeWebSocketUrl when /json/version provides no debugger URL" , async ( ) => {
860856 // Covers the getChromeWebSocketUrl WS-fallback: discovery succeeds but
861857 // webSocketDebuggerUrl is absent — the original URL is returned as-is.
862- vi . stubGlobal (
863- "fetch" ,
864- vi . fn ( ) . mockResolvedValue ( {
865- ok : true ,
866- json : async ( ) => ( { } ) ,
867- } as unknown as Response ) ,
868- ) ;
858+ vi . stubGlobal ( "fetch" , vi . fn ( ) . mockResolvedValue ( jsonResponse ( { } ) ) ) ;
869859 await expect ( getChromeWebSocketUrl ( "ws://127.0.0.1:12345" , 50 ) ) . resolves . toBe (
870860 "ws://127.0.0.1:12345" ,
871861 ) ;
@@ -887,10 +877,7 @@ describe("browser chrome helpers", () => {
887877 it ( "stopOpenClawChrome escalates to SIGKILL when CDP stays reachable" , async ( ) => {
888878 vi . stubGlobal (
889879 "fetch" ,
890- vi . fn ( ) . mockResolvedValue ( {
891- ok : true ,
892- json : async ( ) => ( { webSocketDebuggerUrl : "ws://127.0.0.1/devtools" } ) ,
893- } as unknown as Response ) ,
880+ vi . fn ( ) . mockResolvedValue ( jsonResponse ( { webSocketDebuggerUrl : "ws://127.0.0.1/devtools" } ) ) ,
894881 ) ;
895882 const proc = makeChromeTestProc ( ) ;
896883 await stopChromeWithProc ( proc , 1 ) ;
@@ -915,10 +902,7 @@ describe("browser chrome helpers", () => {
915902 it ( "stopOpenClawChrome still releases the bypass when the SIGKILL fallback fires" , async ( ) => {
916903 vi . stubGlobal (
917904 "fetch" ,
918- vi . fn ( ) . mockResolvedValue ( {
919- ok : true ,
920- json : async ( ) => ( { webSocketDebuggerUrl : "ws://127.0.0.1/devtools" } ) ,
921- } as unknown as Response ) ,
905+ vi . fn ( ) . mockResolvedValue ( jsonResponse ( { webSocketDebuggerUrl : "ws://127.0.0.1/devtools" } ) ) ,
922906 ) ;
923907 const proc = makeChromeTestProc ( ) ;
924908 const release = vi . fn ( ) ;
0 commit comments