fix(desktop): make diagnostic export user-visible

This commit is contained in:
Qiuner
2026-08-17 19:19:39 +08:00
parent fb57baed81
commit 940bbef9dc
2 changed files with 60 additions and 1 deletions
+22 -1
View File
@@ -88,6 +88,7 @@ export class ElectronDesktopRuntime implements DesktopRuntime {
private quitting = false
private readonly trayItems = new Map<symbol, DesktopTrayItem>()
private terminalSpec: DesktopTerminalSpec | undefined
private diagnosticExport: Promise<void> | undefined
private rendererBootReported = false
constructor(
@@ -216,13 +217,22 @@ export class ElectronDesktopRuntime implements DesktopRuntime {
/** @inheritdoc */
exportDiagnostics(): Promise<void> {
if (this.diagnosticExport !== undefined) return this.diagnosticExport
const operation = this.performDiagnosticExport().finally(() => {
if (this.diagnosticExport === operation) this.diagnosticExport = undefined
})
this.diagnosticExport = operation
return operation
}
private performDiagnosticExport(): Promise<void> {
return exportDiagnosticsZip(
join(app.getPath('userData'), 'logs'),
app.getPath('userData'),
).then((path) => {
shell.showItemInFolder(path)
}).catch((cause: unknown) => {
this.logError(`dsh-plugin-desktop: failed to export diagnostics: ${cause instanceof Error ? cause.message : String(cause)}`)
this.reportDiagnosticExportError(cause)
})
}
@@ -467,6 +477,17 @@ export class ElectronDesktopRuntime implements DesktopRuntime {
}
}
/** Keep diagnostic export failures visible in a packaged GUI process. */
private reportDiagnosticExportError(cause: unknown): void {
const error = cause instanceof Error ? cause : new Error(String(cause))
this.logError(`dsh-plugin-desktop: failed to export diagnostics: ${error.message}`)
try {
dialog.showErrorBox('Unable to Export Diagnostics', error.message)
} catch (dialogCause) {
this.logError(`dsh-plugin-desktop: failed to show diagnostics error: ${dialogCause instanceof Error ? dialogCause.message : String(dialogCause)}`)
}
}
private rebuildTrayMenu(): void {
const tray = this.tray
const spec = this.scheduled
@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { DesktopShellSpec } from '../src/runtime.ts'
const terminal = vi.hoisted(() => ({ open: vi.fn() }))
const diagnostics = vi.hoisted(() => ({ export: vi.fn() }))
const updater = vi.hoisted(() => ({ download: vi.fn() }))
const childProcess = vi.hoisted(() => {
type Listener = (...args: unknown[]) => void
@@ -35,6 +36,10 @@ vi.mock('../src/desktop-terminal.ts', async (importOriginal) => ({
openDesktopTerminal: terminal.open,
}))
vi.mock('../src/diagnostic-export.ts', () => ({
exportDiagnosticsZip: diagnostics.export,
}))
vi.mock('../src/update-download.ts', () => ({
downloadDesktopUpdate: updater.download,
}))
@@ -162,6 +167,7 @@ const electron = vi.hoisted(() => {
shell: {
openExternal: vi.fn(async () => {}),
openPath: vi.fn(async () => ''),
showItemInFolder: vi.fn(),
},
templateIcon,
Tray,
@@ -213,6 +219,7 @@ describe('Electron compatibility runtime', () => {
childProcess.reset()
vi.clearAllMocks()
updater.download.mockReset()
diagnostics.export.mockReset()
electron.loadURL.mockReset()
electron.loadURL.mockResolvedValue(undefined)
electron.dialog.showMessageBox.mockResolvedValue({ response: 0, checkboxChecked: false })
@@ -495,6 +502,37 @@ describe('Electron compatibility runtime', () => {
}
})
it('coalesces concurrent diagnostic exports and reveals the completed archive', async () => {
const { ElectronDesktopRuntime } = await import('../src/electron-runtime.ts')
const runtime = new ElectronDesktopRuntime(async () => {})
let finishExport: ((path: string) => void) | undefined
diagnostics.export.mockReturnValue(new Promise(resolve => { finishExport = resolve }))
const first = runtime.exportDiagnostics()
const second = runtime.exportDiagnostics()
finishExport?.('C:\\Users\\Example\\diagnostics.zip')
await Promise.all([first, second])
expect(diagnostics.export).toHaveBeenCalledOnce()
expect(electron.shell.showItemInFolder).toHaveBeenCalledOnce()
expect(electron.shell.showItemInFolder).toHaveBeenCalledWith('C:\\Users\\Example\\diagnostics.zip')
})
it('shows a native error when diagnostic export fails', async () => {
const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true)
const { ElectronDesktopRuntime } = await import('../src/electron-runtime.ts')
const runtime = new ElectronDesktopRuntime(async () => {})
diagnostics.export.mockRejectedValueOnce(new Error('disk is full'))
await expect(runtime.exportDiagnostics()).resolves.toBeUndefined()
expect(electron.dialog.showErrorBox).toHaveBeenCalledWith(
'Unable to Export Diagnostics',
'disk is full',
)
expect(stderr).toHaveBeenCalledWith(expect.stringContaining('failed to export diagnostics: disk is full'))
})
it('shows native errors for synchronous and asynchronous terminal launch failures', async () => {
vi.spyOn(process, 'platform', 'get').mockReturnValue('win32')
const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true)