mirror of
https://gh-proxy.org/https://github.com/klinsmaya/workdsh.git
synced 2026-11-11 02:46:56 +08:00
fix(desktop): make diagnostic export user-visible
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user