@@ -31,9 +31,23 @@ function loadMainModule(t, options = {}) {
3131 ipcMainHandlers . set ( channel , handler ) ;
3232 } ,
3333 } ;
34- const fakeBrowserWindow = {
35- getAllWindows : ( ) => [ ] ,
36- } ;
34+ function defaultBrowserWindow ( ) {
35+ return {
36+ isDestroyed : ( ) => false ,
37+ getAllWindows : ( ) => [ ] ,
38+ setBackgroundColor : ( ) => undefined ,
39+ once : ( ) => undefined ,
40+ webContents : {
41+ on : ( ) => undefined ,
42+ send : ( ) => undefined ,
43+ setWindowOpenHandler : ( ) => undefined ,
44+ } ,
45+ loadFile : async ( ) => undefined ,
46+ loadURL : async ( ) => undefined ,
47+ } ;
48+ }
49+ defaultBrowserWindow . getAllWindows = ( ) => [ ] ;
50+ const fakeBrowserWindow = options . browserWindow || defaultBrowserWindow ;
3751 const fakeNativeTheme = {
3852 shouldUseDarkColors : false ,
3953 on : ( ) => undefined ,
@@ -51,6 +65,15 @@ function loadMainModule(t, options = {}) {
5165 nativeTheme : fakeNativeTheme ,
5266 } ;
5367 }
68+ if ( request === 'http' && options . http ) {
69+ return options . http ;
70+ }
71+ if ( request === 'net' && options . net ) {
72+ return options . net ;
73+ }
74+ if ( request === 'child_process' && options . childProcess ) {
75+ return options . childProcess ;
76+ }
5477 if ( request === 'electron-updater' && options . electronUpdater ) {
5578 return {
5679 autoUpdater : options . electronUpdater ,
@@ -484,6 +507,161 @@ test('restorePackagedRuntimeStateFromBackup skips backup when app version did no
484507 assert . equal ( fs . existsSync ( manifestPath ) , false ) ;
485508} ) ;
486509
510+ test ( 'createWindow startup path does not throw ReferenceError after restore result handling' , async ( t ) => {
511+ const tempRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'dsa-desktop-startup-' ) ) ;
512+ const appDir = path . join ( tempRoot , 'app' ) ;
513+ const userDataDir = path . join ( tempRoot , 'userData' ) ;
514+ const exePath = path . join ( appDir , 'Daily Stock Analysis.exe' ) ;
515+ const uninstallPath = path . join ( appDir , 'Uninstall Daily Stock Analysis.exe' ) ;
516+ const loadedFiles = [ ] ;
517+ const loadedUrls = [ ] ;
518+ let startupError ;
519+ let updateCheckRequested = false ;
520+ const originalResourcesPathDescriptor = Object . getOwnPropertyDescriptor ( process , 'resourcesPath' ) ;
521+ const resourcesPath = path . join ( tempRoot , 'resources' ) ;
522+
523+ function fakeBrowserWindow ( ) {
524+ return {
525+ isDestroyed : ( ) => false ,
526+ setBackgroundColor : ( ) => undefined ,
527+ once : ( ) => undefined ,
528+ webContents : {
529+ on : ( ) => undefined ,
530+ setWindowOpenHandler : ( ) => undefined ,
531+ send : ( ) => undefined ,
532+ } ,
533+ loadFile : async ( file ) => {
534+ loadedFiles . push ( file ) ;
535+ return undefined ;
536+ } ,
537+ loadURL : async ( url ) => {
538+ loadedUrls . push ( url ) ;
539+ return undefined ;
540+ } ,
541+ } ;
542+ }
543+
544+ const fakeBackendProcess = new EventEmitter ( ) ;
545+ fakeBackendProcess . pid = 12345 ;
546+ fakeBackendProcess . exitCode = null ;
547+ fakeBackendProcess . signalCode = null ;
548+ fakeBackendProcess . stdout = new EventEmitter ( ) ;
549+ fakeBackendProcess . stderr = new EventEmitter ( ) ;
550+
551+ const fakeWhenReady = ( ) => ( {
552+ then : ( handler ) => {
553+ return Promise . resolve ( )
554+ . then ( ( ) => handler ( ) )
555+ . catch ( ( error ) => {
556+ startupError = error ;
557+ } ) ;
558+ } ,
559+ } ) ;
560+
561+ const fakeNet = {
562+ createServer : ( ) => {
563+ const server = new EventEmitter ( ) ;
564+ server . once = ( event , handler ) => {
565+ server . on ( event , handler ) ;
566+ return server ;
567+ } ;
568+ server . listen = ( ) => {
569+ process . nextTick ( ( ) => {
570+ server . emit ( 'listening' ) ;
571+ } ) ;
572+ return server ;
573+ } ;
574+ server . close = ( callback ) => {
575+ if ( callback ) {
576+ process . nextTick ( callback ) ;
577+ }
578+ } ;
579+ return server ;
580+ } ,
581+ } ;
582+
583+ const fakeHttp = {
584+ get : ( _url , onResponse ) => {
585+ const request = new EventEmitter ( ) ;
586+ const response = new EventEmitter ( ) ;
587+ request . setTimeout = ( ) => undefined ;
588+ request . destroy = ( ) => undefined ;
589+ response . statusCode = 200 ;
590+ response . resume = ( ) => undefined ;
591+ process . nextTick ( ( ) => {
592+ onResponse ( response ) ;
593+ } ) ;
594+ return request ;
595+ } ,
596+ } ;
597+
598+ if ( originalResourcesPathDescriptor ) {
599+ Object . defineProperty ( process , 'resourcesPath' , {
600+ ...originalResourcesPathDescriptor ,
601+ value : resourcesPath ,
602+ } ) ;
603+ } else {
604+ process . resourcesPath = resourcesPath ;
605+ }
606+
607+ fs . mkdirSync ( appDir , { recursive : true } ) ;
608+ fs . mkdirSync ( userDataDir , { recursive : true } ) ;
609+ fs . mkdirSync ( path . join ( resourcesPath , 'backend' , 'stock_analysis' ) , { recursive : true } ) ;
610+ fs . writeFileSync ( exePath , '' ) ;
611+ fs . writeFileSync ( uninstallPath , '' ) ;
612+ fs . writeFileSync ( path . join ( resourcesPath , 'backend' , 'stock_analysis' , 'stock_analysis.exe' ) , '' ) ;
613+
614+ const mainModule = loadMainModule ( t , {
615+ platform : 'win32' ,
616+ browserWindow : fakeBrowserWindow ,
617+ http : fakeHttp ,
618+ net : fakeNet ,
619+ childProcess : {
620+ spawn : ( ) => fakeBackendProcess ,
621+ } ,
622+ app : {
623+ isPackaged : true ,
624+ getVersion : ( ) => '3.12.0' ,
625+ getPath : ( name ) => {
626+ if ( name === 'exe' ) {
627+ return exePath ;
628+ }
629+ return userDataDir ;
630+ } ,
631+ whenReady : fakeWhenReady ,
632+ on : ( ) => undefined ,
633+ quit : ( ) => undefined ,
634+ } ,
635+ electronUpdater : {
636+ autoDownload : true ,
637+ autoInstallOnAppQuit : false ,
638+ on : ( ) => undefined ,
639+ checkForUpdates : async ( ) => {
640+ updateCheckRequested = true ;
641+ return undefined ;
642+ } ,
643+ } ,
644+ } ) ;
645+
646+ await new Promise ( ( resolve ) => {
647+ setTimeout ( resolve , 80 ) ;
648+ } ) ;
649+
650+ assert . equal ( loadedFiles . length >= 1 , true ) ;
651+ assert . equal ( loadedUrls . length >= 1 , true ) ;
652+ assert . equal ( updateCheckRequested , true ) ;
653+ assert . equal ( startupError , undefined ) ;
654+
655+ t . after ( ( ) => {
656+ if ( originalResourcesPathDescriptor ) {
657+ Object . defineProperty ( process , 'resourcesPath' , originalResourcesPathDescriptor ) ;
658+ } else {
659+ delete process . resourcesPath ;
660+ }
661+ fs . rmSync ( tempRoot , { recursive : true , force : true } ) ;
662+ } ) ;
663+ } ) ;
664+
487665test ( 'stopBackend waits for backend process exit' , async ( t ) => {
488666 const mainModule = loadMainModule ( t ) ;
489667 const killSignals = [ ] ;
0 commit comments