@@ -18,6 +18,7 @@ import {
1818 createProcessRecord ,
1919 getAllProcesses ,
2020 getAllowedRoots ,
21+ getElectronDebugInfo ,
2122 getProcess ,
2223 isConsoleLiveLoggingEnabled ,
2324 listProcesses ,
@@ -768,3 +769,159 @@ test("validateOutputPath splits allowlist on both ';' and '|'", () => {
768769 restoreEnv ( prev ) ;
769770 }
770771} ) ;
772+
773+ // ===========================================================================
774+ // getElectronDebugInfo
775+ // ===========================================================================
776+ // Builds a debug summary (target role counts, webContents list, recent
777+ // console errors) from a process record. Pure once the process exists in
778+ // the registry -- we insert via getAllProcesses() and use a stopped/no-port
779+ // process so updateCDPTargets (network) is skipped.
780+
781+ test ( "getElectronDebugInfo returns null for an unknown id" , async ( ) => {
782+ const info = await getElectronDebugInfo ( "definitely-not-present" ) ;
783+ assert . equal ( info , null ) ;
784+ } ) ;
785+
786+ test ( "getElectronDebugInfo counts targets by role in targetSummary" , async ( ) => {
787+ const proc = makeProc ( {
788+ id : "info-roles" ,
789+ status : "stopped" , // no debugPort call path -> stays pure
790+ debugPort : undefined ,
791+ targets : makeTargets ( [
792+ { id : "p1" , type : "page" } ,
793+ { id : "p2" , type : "page" } ,
794+ { id : "sw" , type : "service_worker" } , // -> worker
795+ { id : "br" , type : "browser" } ,
796+ { id : "if" , type : "iframe" } , // -> other
797+ ] ) ,
798+ } ) ;
799+ getAllProcesses ( ) . set ( proc . id , proc ) ;
800+ try {
801+ const info = await getElectronDebugInfo ( proc . id ) ;
802+ assert . deepEqual ( info . targetSummary , {
803+ pages : 2 ,
804+ workers : 1 ,
805+ browser : 1 ,
806+ other : 1 ,
807+ } ) ;
808+ } finally {
809+ getAllProcesses ( ) . delete ( proc . id ) ;
810+ }
811+ } ) ;
812+
813+ test ( "getElectronDebugInfo maps webContents with debuggable flag from ws url" , async ( ) => {
814+ const proc = makeProc ( {
815+ id : "info-debuggable" ,
816+ status : "stopped" ,
817+ debugPort : undefined ,
818+ targets : makeTargets ( [
819+ { id : "a" , type : "page" , webSocketDebuggerUrl : "ws://127.0.0.1:9/page/a" } ,
820+ { id : "b" , type : "page" } , // no ws url -> not debuggable
821+ ] ) ,
822+ } ) ;
823+ getAllProcesses ( ) . set ( proc . id , proc ) ;
824+ try {
825+ const info = await getElectronDebugInfo ( proc . id ) ;
826+ assert . equal ( info . webContents . length , 2 ) ;
827+ assert . equal ( info . webContents [ 0 ] . debuggable , true ) ;
828+ assert . equal ( info . webContents [ 1 ] . debuggable , false ) ;
829+ // webContents ids are 1-based positional.
830+ assert . deepEqual (
831+ info . webContents . map ( ( w ) => w . id ) ,
832+ [ 1 , 2 ]
833+ ) ;
834+ assert . equal ( info . webContents [ 0 ] . targetId , "a" ) ;
835+ } finally {
836+ getAllProcesses ( ) . delete ( proc . id ) ;
837+ }
838+ } ) ;
839+
840+ test ( "getElectronDebugInfo filters recentConsoleErrors to errors/exceptions only" , async ( ) => {
841+ const proc = makeProc ( { id : "info-errors" , status : "stopped" , debugPort : undefined } ) ;
842+ proc . consoleMessages . push (
843+ { timestamp : "1" , targetId : "t" , level : "log" , text : "fine" , source : "log" } ,
844+ { timestamp : "2" , targetId : "t" , level : "error" , text : "boom" , source : "console" } ,
845+ { timestamp : "3" , targetId : "t" , level : "warning" , text : "hmm" , source : "log" } ,
846+ { timestamp : "4" , targetId : "t" , level : "info" , text : "oops" , source : "exception" }
847+ ) ;
848+ getAllProcesses ( ) . set ( proc . id , proc ) ;
849+ try {
850+ const info = await getElectronDebugInfo ( proc . id ) ;
851+ // Only the error-level and exception-source messages survive.
852+ assert . equal ( info . recentConsoleErrors . length , 2 ) ;
853+ assert . deepEqual (
854+ info . recentConsoleErrors . map ( ( m ) => m . text ) ,
855+ [ "boom" , "oops" ]
856+ ) ;
857+ } finally {
858+ getAllProcesses ( ) . delete ( proc . id ) ;
859+ }
860+ } ) ;
861+
862+ test ( "getElectronDebugInfo caps recentConsoleErrors at the last 10" , async ( ) => {
863+ const proc = makeProc ( { id : "info-cap" , status : "stopped" , debugPort : undefined } ) ;
864+ for ( let i = 0 ; i < 15 ; i ++ ) {
865+ proc . consoleMessages . push ( {
866+ timestamp : String ( i ) ,
867+ targetId : "t" ,
868+ level : "error" ,
869+ text : `err-${ i } ` ,
870+ source : "console" ,
871+ } ) ;
872+ }
873+ getAllProcesses ( ) . set ( proc . id , proc ) ;
874+ try {
875+ const info = await getElectronDebugInfo ( proc . id ) ;
876+ assert . equal ( info . recentConsoleErrors . length , 10 ) ;
877+ // slice(-10) keeps the most recent.
878+ assert . equal ( info . recentConsoleErrors [ 0 ] . text , "err-5" ) ;
879+ assert . equal ( info . recentConsoleErrors [ 9 ] . text , "err-14" ) ;
880+ } finally {
881+ getAllProcesses ( ) . delete ( proc . id ) ;
882+ }
883+ } ) ;
884+
885+ test ( "getElectronDebugInfo handles a process with no targets" , async ( ) => {
886+ const proc = makeProc ( {
887+ id : "info-notargets" ,
888+ status : "stopped" ,
889+ debugPort : undefined ,
890+ targets : undefined ,
891+ } ) ;
892+ getAllProcesses ( ) . set ( proc . id , proc ) ;
893+ try {
894+ const info = await getElectronDebugInfo ( proc . id ) ;
895+ assert . deepEqual ( info . targetSummary , {
896+ pages : 0 ,
897+ workers : 0 ,
898+ browser : 0 ,
899+ other : 0 ,
900+ } ) ;
901+ assert . deepEqual ( info . webContents , [ ] ) ;
902+ } finally {
903+ getAllProcesses ( ) . delete ( proc . id ) ;
904+ }
905+ } ) ;
906+
907+ test ( "getElectronDebugInfo echoes process identity fields" , async ( ) => {
908+ const proc = makeProc ( {
909+ id : "info-id" ,
910+ name : "my-electron-app" ,
911+ status : "stopped" ,
912+ debugPort : undefined ,
913+ pid : 4242 ,
914+ appPath : "/tmp/myapp" ,
915+ } ) ;
916+ getAllProcesses ( ) . set ( proc . id , proc ) ;
917+ try {
918+ const info = await getElectronDebugInfo ( proc . id ) ;
919+ assert . equal ( info . id , "info-id" ) ;
920+ assert . equal ( info . name , "my-electron-app" ) ;
921+ assert . equal ( info . status , "stopped" ) ;
922+ assert . equal ( info . pid , 4242 ) ;
923+ assert . equal ( info . appPath , "/tmp/myapp" ) ;
924+ } finally {
925+ getAllProcesses ( ) . delete ( proc . id ) ;
926+ }
927+ } ) ;
0 commit comments