Update: Created Minimal reproducible example on GitHub.
I'm having an issue with mocking ipcMain in wdio + electron + mocha. I have a running electron app that I'm trying to e2e test, but I'm unable to intercept / replace ipcMain message responses.
I may not actually need to replace the ipcMain responses if I can otherwise pass the value I want, but I think mocking out ipcMain is the way to go. I don't seem to have access to the Main thread objects to mock them, is that true?
Here's the relevant part of my app, which is written in Vue:
<template>
<div class="plugin__output" v-if="outputMessage">
{{ outputMessage }}
</div>
</template>
<script start lang="ts">
import { ref, defineComponent, Ref, computed } from "vue"
declare const electronAPI: {
sendMessage: (message: string) => Promise<string>
}
export default defineComponent({
name: "PathChecker",
setup() {
const outputMessage: Ref<string> = ref("")
async function checkIsAvailable(): Promise<void> {
const response = await electronAPI.sendMessage(
"plugin:PathChecker:availability"
)
if (response === "true") {
outputMessage.value = ""
return
}
outputMessage.value = `Plugin disabled: ${response}`
}
async function handleClick(): Promise<void> {
outputMessage.value = ""
const response = await electronAPI.sendMessage(
"plugin:PathChecker:performCheck"
)
// The actual app does other stuff here, this is for the demo
outputMessage.value = response;
}
// Check if this plugin is available at creation time.
checkIsAvailable()
return {
outputMessage,
}
},
})
</script>
I've written the following test:
import { browser, expect } from "@wdio/globals"
describe('MyComponent', () => {
it("Should have an disabled button if plugin:PathChecker:availability returns anything but 'true'", async () => {
// Attempt to return "mocked" instead of "true".
// This mock is never called.
const ipcMock = await browser.electron.mock("ipcMain", "handle", "mocked")
const pluginPathChecker = browser.$(".plugin--path-checker")
await expect(
await pluginPathChecker.$(".plugin__header__button").isEnabled()
).toBeFalsy()
expect(ipcMock).toHaveBeenCalledTimes(1);
})
});
However, the browser.electron.mock
call doesn't seem to do anything at all. My best guess is that I'm mocking it too late - the component has already run checkIsAvailable()
by the time my test is running.
I've also tried adding this instead of the mock
method at the start of my test, but with no effect.
await browser.electron.execute((electron) => {
electron.ipcMain.removeHandler("message")
electron.ipcMain.handle("message", async () => "mocked")
})
Let me know if there's anything else I can provide to clarify or help!
[Update 2025-01-30] I spent another hour hacking away at this and I think I know better what my actual questions are:
- When using wdio + electron, is there a way to access variables on the main process?
- or, is there a way to mock/fake ipc calls between the main process and the render process?
- or, is there a way to mock out functions on my vue component before rendering it?
I think solving any of these (or maybe there's another way!) will get me to what I want, which is to control the information that the vue component receives from the main process
Note that this question is similar to this question, but with different context. A solution to this one might solve that one as well.