UNPKG

electron-playwright-helpers

Version:

Helper functions for Electron end-to-end testing using Playwright

226 lines 9.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ipcMainInvokeHandler = exports.ipcMainCallFirstListener = exports.ipcMainEmit = exports.ipcRendererEmit = exports.ipcRendererCallFirstListener = exports.ipcRendererInvoke = exports.ipcRendererSend = void 0; /** * Send an `ipcRenderer.send()` (to main process) from a given window. * * Note: nodeIntegration must be true and contextIsolation must be false * in the webPreferences for this BrowserWindow. * * @category IPCRenderer * * @param page {Page} the Playwright Page to send the ipcRenderer.send() from * @param channel {string} the channel to send the ipcRenderer.send() to * @param args {...unknown} one or more arguments to send to the `ipcRenderer.send()` * @returns {Promise<unknown>} * @fulfil {unknown} resolves with the result of `ipcRenderer.send()` */ function ipcRendererSend(page, channel, ...args) { return page.evaluate(({ channel, args }) => { if (!require) { throw new Error(`Cannot access require() in renderer process. Is nodeIntegration: true?`); } // eslint-disable-next-line @typescript-eslint/no-var-requires const { ipcRenderer } = require('electron'); return ipcRenderer.send(channel, ...args); }, { channel, args }); } exports.ipcRendererSend = ipcRendererSend; /** * Send an ipcRenderer.invoke() from a given window. * * Note: nodeIntegration must be true and contextIsolation must be false * in the webPreferences for this window * * @category IPCRenderer * * @param page {Page} the Playwright Page to send the ipcRenderer.invoke() from * @param message {string} the channel to send the ipcRenderer.invoke() to * @param args {...unknown} one or more arguments to send to the ipcRenderer.invoke() * @returns {Promise<unknown>} * @fulfil {unknown} resolves with the result of ipcRenderer.invoke() */ function ipcRendererInvoke(page, message, ...args) { return page.evaluate(async ({ message, args }) => { if (!require) { throw new Error(`Cannot access require() in renderer process. Is nodeIntegration: true?`); } // eslint-disable-next-line @typescript-eslint/no-var-requires const { ipcRenderer } = require('electron'); return await ipcRenderer.invoke(message, ...args); }, { message, args }); } exports.ipcRendererInvoke = ipcRendererInvoke; /** * Call just the first listener for a given ipcRenderer channel in a given window. * *UNLIKE MOST Electron ipcRenderer listeners*, this function SHOULD return a value. * * This function does not send data between main and renderer processes. * It simply retrieves data from the renderer process. * * Note: nodeIntegration must be true for this BrowserWindow. * * @category IPCRenderer * * @param page {Page} The Playwright Page to with the `ipcRenderer.on()` listener * @param message {string} The channel to call the first listener for * @param args {...unknown} optional - One or more arguments to send to the ipcRenderer.on() listener * @returns {Promise<unknown>} * @fulfil {unknown} the result of the first `ipcRenderer.on()` listener */ async function ipcRendererCallFirstListener(page, message, ...args) { const result = await page.evaluate(async ({ message, args }) => { if (!require) { throw new Error(`Cannot access require() in renderer process. Is nodeIntegration: true?`); } // eslint-disable-next-line @typescript-eslint/no-var-requires const { ipcRenderer } = require('electron'); if (ipcRenderer.listenerCount(message) > 0) { // we send a fake event in place of the ipc event object const event = {}; // also, we await in case the listener returns a promise return await ipcRenderer.listeners(message)[0](event, ...args); } else { throw new Error(`No ipcRenderer listeners for '${message}'`); } }, { message, args }); // console.log(`ipcRendererCallFirstListener(${message}) result: ${result} `) return result; } exports.ipcRendererCallFirstListener = ipcRendererCallFirstListener; /** * Emit an IPC message to a given window. * This will trigger all ipcRenderer listeners for the message. * * This does not transfer data between main and renderer processes. * It simply emits an event in the renderer process. * * Note: nodeIntegration must be true for this window * * @category IPCRenderer * * @param page {Page} - the Playwright Page to with the ipcRenderer.on() listener * @param message {string} - the channel to call all ipcRenderer listeners for * @param args {...unknown} optional - one or more arguments to send * @returns {Promise<boolean>} * @fulfil {boolean} true if the event was emitted * @reject {Error} if there are no ipcRenderer listeners for the event */ function ipcRendererEmit(page, message, ...args) { return page.evaluate(({ message, args }) => { if (!require) { throw new Error(`Cannot access require() in renderer process. Is nodeIntegration: true?`); } // eslint-disable-next-line @typescript-eslint/no-var-requires const { ipcRenderer } = require('electron'); if (ipcRenderer.listenerCount(message) === 0) { throw new Error(`No ipcRenderer listeners for '${message}'`); } // create a fake event object const event = {}; return ipcRenderer.emit(message, event, ...args); }, { message, args }); } exports.ipcRendererEmit = ipcRendererEmit; /** * Emit an ipcMain message from the main process. * This will trigger all ipcMain listeners for the message. * * This does not transfer data between main and renderer processes. * It simply emits an event in the main process. * * @category IPCMain * * @param electronApp {ElectronApplication} - the ElectronApplication object from Playwright * @param message {string} - the channel to call all ipcMain listeners for * @param args {...unknown} - one or more arguments to send * @returns {Promise<boolean>} * @fulfil {boolean} true if there were listeners for this message * @reject {Error} if there are no ipcMain listeners for the event */ function ipcMainEmit(electronApp, message, ...args) { return electronApp.evaluate(({ ipcMain }, { message, args }) => { if (ipcMain.listeners(message).length > 0) { // fake ipcMainEvent const event = {}; return ipcMain.emit(message, event, ...args); } else { throw new Error(`No ipcMain listeners for '${message}'`); } }, { message, args }); } exports.ipcMainEmit = ipcMainEmit; /** * Call the first listener for a given ipcMain message in the main process * and return its result. * * NOTE: ipcMain listeners usually don't return a value, but we're using * this to retrieve test data from the main process. * * Generally, it's probably better to use `ipcMainInvokeHandler()` instead. * * @category IPCMain * * @param electronApp {ElectronApplication} - the ElectronApplication object from Playwright * @param message {string} - the channel to call the first listener for * @param args {...unknown} - one or more arguments to send * @returns {Promise<unknown>} * @fulfil {unknown} resolves with the result of the function * @reject {Error} if there are no ipcMain listeners for the event */ async function ipcMainCallFirstListener(electronApp, message, ...args) { return await electronApp.evaluate(async ({ ipcMain }, { message, args }) => { if (ipcMain.listenerCount(message) > 0) { // fake ipcMainEvent const event = {}; return await ipcMain.listeners(message)[0](event, ...args); } else { throw new Error(`No listeners for message ${message}`); } }, { message, args }); } exports.ipcMainCallFirstListener = ipcMainCallFirstListener; /** * Get the return value of an `ipcMain.handle()` function * * @category IPCMain * * @param electronApp {ElectronApplication} - the ElectronApplication object from Playwright * @param message {string} - the channel to call the first listener for * @param args {...unknown} - one or more arguments to send * @returns {Promise<unknown>} * @fulfil {unknown} resolves with the result of the function called in main process */ async function ipcMainInvokeHandler(electronApp, message, ...args) { return await electronApp.evaluate(async ({ ipcMain }, { message, args }) => { var _a; const ipcMainWH = ipcMain; // this is all a bit of a hack, so let's test as we go if (!ipcMainWH._invokeHandlers) { throw new Error(`Cannot access ipcMain._invokeHandlers`); } const handler = ipcMainWH._invokeHandlers.get(message); if (!handler) { throw new Error(`No ipcMain handler registered for '${message}'`); } // in electron <= 24, the event object's _reply() method is called let e24reply; const e = {}; e._reply = (value) => { e24reply = value; }; e._throw = function (error) { throw error; }; // in electron >= 25, we can simply call the handler const e25reply = await handler(e, ...args); // return the value from the event object if it exists // otherwise return the value from the handler return (_a = (await e24reply)) !== null && _a !== void 0 ? _a : e25reply; }, { message, args }); } exports.ipcMainInvokeHandler = ipcMainInvokeHandler; //# sourceMappingURL=ipc_helpers.js.map