mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 18:10:18 +05:30
feat(renderer): add CrashReportModal listing local dumps
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { ipc } from '@/lib/ipc';
|
||||||
|
|
||||||
|
interface Dump {
|
||||||
|
filename: string;
|
||||||
|
kind: string;
|
||||||
|
message?: string;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CrashReportModal({ onClose }: { onClose: () => void }) {
|
||||||
|
const [dumps, setDumps] = useState<Dump[]>([]);
|
||||||
|
|
||||||
|
const refresh = async () => setDumps(await ipc.crash.read());
|
||||||
|
useEffect(() => { refresh(); }, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div role="dialog" aria-modal="true" data-testid="crash-report-modal" className="fixed inset-0 z-50 bg-black/40 flex items-center justify-center">
|
||||||
|
<div className="bg-white dark:bg-neutral-900 rounded-lg p-6 w-[36rem] max-h-[80vh] overflow-y-auto shadow-xl">
|
||||||
|
<header className="flex items-center justify-between mb-4">
|
||||||
|
<h2 className="text-lg font-semibold">Crash reports</h2>
|
||||||
|
<button onClick={onClose} aria-label="Close">×</button>
|
||||||
|
</header>
|
||||||
|
<button onClick={() => ipc.crash.openDir()} className="mb-4 px-3 py-1 text-sm border rounded">
|
||||||
|
Open dump folder
|
||||||
|
</button>
|
||||||
|
{dumps.length === 0 ? (
|
||||||
|
<p data-testid="empty-state" className="text-neutral-500">No crashes recorded — nice work!</p>
|
||||||
|
) : (
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{dumps.map((d) => (
|
||||||
|
<li key={d.filename} className="border rounded p-2 text-sm flex justify-between items-start gap-2">
|
||||||
|
<div>
|
||||||
|
<div className="font-mono text-xs text-neutral-500">{d.timestamp}</div>
|
||||||
|
<div>{d.message ?? '(no message)'}</div>
|
||||||
|
</div>
|
||||||
|
<button onClick={async () => { await ipc.crash.delete(d.filename); refresh(); }} className="text-red-600 text-xs">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { describe, expect, test, vi } from 'vitest';
|
||||||
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||||
|
import { CrashReportModal } from '@/components/modals/CrashReportModal';
|
||||||
|
import { ipc } from '@/lib/ipc';
|
||||||
|
|
||||||
|
vi.mock('@/lib/ipc', () => ({
|
||||||
|
ipc: {
|
||||||
|
crash: { read: vi.fn(), openDir: vi.fn(), delete: vi.fn() },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('CrashReportModal', () => {
|
||||||
|
test('shows empty state when no crashes', async () => {
|
||||||
|
(ipc.crash.read as any).mockResolvedValue([]);
|
||||||
|
render(<CrashReportModal onClose={() => {}} />);
|
||||||
|
expect(await screen.findByText(/no crashes/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('lists crashes returned by ipc', async () => {
|
||||||
|
(ipc.crash.read as any).mockResolvedValue([
|
||||||
|
{ filename: '1700000000000-1-uncaughtException.json', kind: 'uncaughtException', message: 'boom', timestamp: '2026-01-01T00:00:00.000Z' },
|
||||||
|
]);
|
||||||
|
render(<CrashReportModal onClose={() => {}} />);
|
||||||
|
expect(await screen.findByText(/boom/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('delete button calls ipc.crash.delete', async () => {
|
||||||
|
(ipc.crash.read as any).mockResolvedValue([
|
||||||
|
{ filename: '1700000000000-1-uncaughtException.json', kind: 'uncaughtException', message: 'boom', timestamp: '2026-01-01T00:00:00.000Z' },
|
||||||
|
]);
|
||||||
|
render(<CrashReportModal onClose={() => {}} />);
|
||||||
|
const btn = await screen.findByText(/delete/i);
|
||||||
|
fireEvent.click(btn);
|
||||||
|
await waitFor(() => expect(ipc.crash.delete).toHaveBeenCalledWith('1700000000000-1-uncaughtException.json'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('open folder button calls ipc.crash.openDir', async () => {
|
||||||
|
(ipc.crash.read as any).mockResolvedValue([]);
|
||||||
|
render(<CrashReportModal onClose={() => {}} />);
|
||||||
|
fireEvent.click(await screen.findByText(/open dump folder/i));
|
||||||
|
expect(ipc.crash.openDir).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user