Skip to content

Commit afbc466

Browse files
committed
fix(review-feedback-1256): address latest review comments
1 parent 625fdff commit afbc466

3 files changed

Lines changed: 187 additions & 9 deletions

File tree

apps/dsa-desktop/main.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,17 +1397,17 @@ ipcMain.handle('desktop:open-release-page', async (_event, releaseUrl) => {
13971397
async function createWindow() {
13981398
const restoreResult = isWindowsNsisInstalledApp() ? restorePackagedRuntimeStateFromBackup() : null;
13991399
initLogging();
1400-
const restoreNeedsAttention = Boolean(restoreResult && (restoreResult.failed.length || restoreResult.skipped.length));
1400+
const restoreFailed = Boolean(restoreResult && (restoreResult.failed.length || restoreResult.skipped.length));
14011401
const restoreIssueDetails = restoreResult
14021402
? restoreResult.failed.concat(restoreResult.skipped).join(';')
14031403
: '';
1404-
const restoreErrorMessage = restoreNeedsAttention
1404+
const restoreErrorMessage = restoreFailed
14051405
? `上次更新安装未完成或恢复运行时文件失败,已保留备份目录 ${restoreResult.backupRoot},请确认后手动恢复并重启应用。明细:${restoreIssueDetails}`
14061406
: '';
14071407
setDesktopUpdateState({
1408-
status: restoreNeedsAttention ? UPDATE_STATUS.ERROR : UPDATE_STATUS.IDLE,
1408+
status: restoreFailed ? UPDATE_STATUS.ERROR : UPDATE_STATUS.IDLE,
14091409
currentVersion: resolveDesktopVersion(),
1410-
updateMode: restoreNeedsAttention ? UPDATE_MODE.MANUAL : UPDATE_MODE.AUTO,
1410+
updateMode: restoreFailed ? UPDATE_MODE.MANUAL : UPDATE_MODE.AUTO,
14111411
message: restoreErrorMessage,
14121412
});
14131413
const startupStartedAt = Date.now();

apps/dsa-desktop/tests/main.test.js

Lines changed: 181 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
487665
test('stopBackend waits for backend process exit', async (t) => {
488666
const mainModule = loadMainModule(t);
489667
const killSignals = [];

scripts/verify-desktop-updater-artifacts.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
$ErrorActionPreference = 'Stop'
2-
31
param(
42
[string]$DistDir = '',
53
[string]$ReleaseTag = ''
64
)
75

6+
$ErrorActionPreference = 'Stop'
7+
88
function Resolve-ReleaseTag {
99
param([string]$Tag)
1010

0 commit comments

Comments
 (0)