agent-ecosystem/test/main/utils/safeWebContentsSend.test.ts

94 lines
2.8 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
const { warn } = vi.hoisted(() => ({
warn: vi.fn(),
}));
vi.mock('@shared/utils/logger', () => ({
createLogger: () => ({
warn,
}),
}));
import {
clearRendererAvailability,
markRendererReady,
markRendererUnavailable,
safeSendToRenderer,
} from '../../../src/main/utils/safeWebContentsSend';
import type { BrowserWindow } from 'electron';
function createWindow(options?: {
windowDestroyed?: boolean;
contentsDestroyed?: boolean;
sendImpl?: (...args: unknown[]) => void;
}): BrowserWindow {
return {
isDestroyed: vi.fn(() => options?.windowDestroyed ?? false),
webContents: {
isDestroyed: vi.fn(() => options?.contentsDestroyed ?? false),
send: vi.fn(options?.sendImpl ?? (() => undefined)),
},
} as unknown as BrowserWindow;
}
describe('safeSendToRenderer', () => {
beforeEach(() => {
warn.mockReset();
});
it('sends IPC to a live renderer', () => {
const window = createWindow();
const result = safeSendToRenderer(window, 'test:channel', { ok: true });
expect(result).toBe(true);
expect(window.webContents.send).toHaveBeenCalledWith('test:channel', { ok: true });
});
it('returns false when window is missing or destroyed', () => {
expect(safeSendToRenderer(null, 'test:channel')).toBe(false);
const destroyedWindow = createWindow({ windowDestroyed: true });
expect(safeSendToRenderer(destroyedWindow, 'test:channel')).toBe(false);
expect(destroyedWindow.webContents.send).not.toHaveBeenCalled();
});
it('returns false when webContents is destroyed', () => {
const window = createWindow({ contentsDestroyed: true });
const result = safeSendToRenderer(window, 'test:channel');
expect(result).toBe(false);
expect(window.webContents.send).not.toHaveBeenCalled();
});
it('swallows renderer disposal errors and logs a warning', () => {
const window = createWindow({
sendImpl: () => {
throw new Error('Render frame was disposed before WebFrameMain could be accessed');
},
});
const result = safeSendToRenderer(window, 'test:channel', 123);
expect(result).toBe(false);
expect(warn).toHaveBeenCalledTimes(1);
expect(String(warn.mock.calls[0][0])).toContain('test:channel');
});
it('blocks sends while renderer is unavailable and resumes after ready', () => {
const window = createWindow();
markRendererUnavailable(window);
expect(safeSendToRenderer(window, 'test:channel', 'first')).toBe(false);
expect(window.webContents.send).not.toHaveBeenCalled();
markRendererReady(window);
expect(safeSendToRenderer(window, 'test:channel', 'second')).toBe(true);
expect(window.webContents.send).toHaveBeenCalledWith('test:channel', 'second');
clearRendererAvailability(window);
});
});