Files
gridpilot.gg/tests/smoke/helpers/ipc-verifier.ts
2025-12-12 21:39:48 +01:00

228 lines
6.5 KiB
TypeScript

import { ElectronApplication } from '@playwright/test';
type IpcHandlerResult = {
error?: string;
[key: string]: unknown;
};
export interface IPCTestResult {
channel: string;
success: boolean;
error?: string;
duration: number;
}
/**
* IPCVerifier - Tests IPC channel contracts
*
* Purpose: Verify main <-> renderer communication works
* Scope: Core IPC channels required for app functionality
*/
export class IPCVerifier {
constructor(private app: ElectronApplication) {}
/**
* Test checkAuth IPC channel
*/
async testCheckAuth(): Promise<IPCTestResult> {
const start = Date.now();
const channel = 'auth:check';
try {
const result = await this.app.evaluate(
async ({ ipcMain }: { ipcMain: { listeners: (channel: string) => unknown[] } }) => {
return new Promise((resolve) => {
// Simulate IPC invoke handler by calling the first registered handler for the channel
const handlers = ipcMain.listeners('auth:check') || [];
const handler = handlers[0] as
| ((event: unknown, ...args: unknown[]) => unknown | Promise<unknown>)
| undefined;
if (!handler) {
resolve({ error: 'Handler not registered' });
} else {
// Invoke the handler similar to ipcMain.handle invocation signature
// (event, ...args) => Promise
const mockEvent: unknown = {};
Promise.resolve(handler(mockEvent))
.then((res: unknown) => resolve(res))
.catch((err: unknown) =>
resolve({
error:
err && err instanceof Error && err.message
? err.message
: String(err),
}),
);
}
});
},
);
const typed: IpcHandlerResult = result as IpcHandlerResult;
const resultObj: IPCTestResult = {
channel,
success: !typed.error,
duration: Date.now() - start,
};
if (typed.error) {
resultObj.error = typed.error;
}
return resultObj;
} catch (error) {
return {
channel,
success: false,
error: error instanceof Error ? error.message : String(error),
duration: Date.now() - start,
};
}
}
/**
* Test getBrowserMode IPC channel
*/
async testGetBrowserMode(): Promise<IPCTestResult> {
const start = Date.now();
const channel = 'browser-mode:get';
try {
const result = await this.app.evaluate(
async ({ ipcMain }: { ipcMain: { listeners: (channel: string) => unknown[] } }) => {
return new Promise((resolve) => {
const handlers = ipcMain.listeners('browser-mode:get') || [];
const handler = handlers[0] as
| ((event: unknown, ...args: unknown[]) => unknown | Promise<unknown>)
| undefined;
if (!handler) {
resolve({ error: 'Handler not registered' });
} else {
const mockEvent: unknown = {};
Promise.resolve(handler(mockEvent))
.then((res: unknown) => resolve(res))
.catch((err: unknown) =>
resolve({
error:
err && err instanceof Error && err.message
? err.message
: String(err),
}),
);
}
});
},
);
const typed: IpcHandlerResult = result as IpcHandlerResult;
const resultObj: IPCTestResult = {
channel,
success: !typed.error,
duration: Date.now() - start,
};
if (typed.error) {
resultObj.error = typed.error;
}
return resultObj;
} catch (error) {
return {
channel,
success: false,
error: error instanceof Error ? error.message : String(error),
duration: Date.now() - start,
};
}
}
/**
* Test startAutomationSession IPC channel contract
*/
async testStartAutomationSession(): Promise<IPCTestResult> {
const start = Date.now();
const channel = 'start-automation';
try {
const result = await this.app.evaluate(
async ({ ipcMain }: { ipcMain: { listeners: (channel: string) => unknown[] } }) => {
return new Promise((resolve) => {
const handlers = ipcMain.listeners('start-automation') || [];
const handler = handlers[0] as
| ((
event: unknown,
payload: { sessionName: string; mode: string },
) => unknown | Promise<unknown>)
| undefined;
if (!handler) {
resolve({ error: 'Handler not registered' });
} else {
// Test with mock data
const mockEvent: unknown = {};
Promise.resolve(
handler(mockEvent, { sessionName: 'test', mode: 'test' }),
)
.then((res: unknown) => resolve(res))
.catch((err: unknown) =>
resolve({
error:
err && err instanceof Error && err.message
? err.message
: String(err),
}),
);
}
});
},
);
const typed: IpcHandlerResult = result as IpcHandlerResult;
const resultObj: IPCTestResult = {
channel,
success: !typed.error,
duration: Date.now() - start,
};
if (typed.error) {
resultObj.error = typed.error;
}
return resultObj;
} catch (error) {
return {
channel,
success: false,
error: error instanceof Error ? error.message : String(error),
duration: Date.now() - start,
};
}
}
/**
* Run all IPC tests and return results
*/
async verifyAllChannels(): Promise<IPCTestResult[]> {
return Promise.all([
this.testCheckAuth(),
this.testGetBrowserMode(),
this.testStartAutomationSession(),
]);
}
/**
* Format IPC test results for output
*/
static formatResults(results: IPCTestResult[]): string {
const lines = ['IPC Channel Verification:', ''];
results.forEach(result => {
const status = result.success ? '✓' : '✗';
lines.push(`${status} ${result.channel} (${result.duration}ms)`);
if (result.error) {
lines.push(` Error: ${result.error}`);
}
});
return lines.join('\n');
}
}