spectron
spectron copied to clipboard
Testing menu interaction
Love the framework! 👍
Is there any way currently to test menu item functionality through spectron? I could test the code itself but that doesn't take into account menu item states like enabled
and visible
.
If not, is there a suggested strategy for testing menu items?
Thanks!
If not, is there a suggested strategy for testing menu items?
Not currently, the require('electron').remote.Menu.getApplicationMenu()
API does not serialize to JSON so it can't currently be fetched via Spectron.
This is something that should be added though 👍
@atdrago wondering how you test the menu functionality like click on the menu item?
If this functionality is added then that would be nice and looking forward for this implementation.
Thanks.
@navinjoy The way I did it is not great. It relies on there being a renderer process which makes it very difficult to do anything (besides app.restart()
) when all windows are closed. But here's what I did. I'm open to suggestions if anyone knows a better way!
In the main process
- Inject some ipcMain listeners when NODE_ENV=development https://github.com/atdrago/negative/blob/develop/lib/negative.js#L47-L49
- The listeners call a
simulateMenuClick
function https://github.com/atdrago/negative/blob/develop/lib/negative-test.js#L35-L72 -
simulateMenuClick
checks if themenuItem
is enabled and visible, and then calls itsclick
handler if it has one https://github.com/atdrago/negative/blob/develop/lib/negative-test.js#L27-L29
In the tests
4. Use ipcRenderer to send menuItem
messages to ipcMain. For example, testing "Zoom In":
https://github.com/atdrago/negative/blob/develop/test/test.js#L233-L255
A couple downfalls of this approach
- As mentioned above, this fails when there are no windows (no renderer processes), so testing things that close and open new windows is painful
- If the
menuItem
usesrole
orperformSelector
, there's no way to test it
I created a small demo app to show how I implemented my tests. Much easier to digest than looking at Negative. It's actually working quite well now, except for the fact that I still can't test menus that don't have a click
handler. It's written for Node v4.4.4 and Electron v0.37.8. @navinjoy I hope this helps you and your team :)
https://github.com/atdrago/electron-testing-legacy
@atdrago Thanks a lot for sharing this... we will try and let you know how it goes....have a great day 👍
thanks adam ...will try this and let u know how it goes ...this totally works !!! big thank you to adam !
Not currently, the require('electron').remote.Menu.getApplicationMenu() API does not serialize to JSON so it can't currently be fetched via Spectron.
@kevinsawicki Should I file a new issue under Electron for this?
Should I file a new issue under Electron for this?
I don't think so, it isn't really an issue in Electron, many APIs may return objects that don't serialize to JSON.
It should probably be fixed here instead.
Has there been an update on this? I has a preferences window in my app that is activated by a menu item and would like to be able to test it.
Thanks!
In Ember acceptance tests, I want to click a customized menu option. Is it possible? How do I do that?
I am trying to test my electron app - menu functionality using spectron and I wanted to know if there is any update...
Thanks
I don't know if anything has changed since this issue was created, regarding the getApplicationMenu()
output. When I try to get the application menu from Spectron it will throw an Maximum call stack size exceeded
error. It looks like there's a circular reference in the menu, each menu item got an property named menu
which references to its parent. That should be possible to solve, but I don't have time right now.
This is what I tried:
it('should open menu item', async function () {
await this.app.client.waitUntilWindowLoaded()
.pause(3000);
const menu = await this.app.electron.remote.Menu.getApplicationMenu();
});
Any updates on this one ?
Found some workaround.
In application, when creating menu:
var menu = Menu.buildFromTemplate(template);
menu.getItemByNames = function (...names) {
var currMenu = this;
var parentLabels = [];
var result = null;
for (let name of names) {
parentLabels.push(name);
var foundMenu = null;
for (var i = 0; i <= currMenu.getItemCount(); i++) {
if (currMenu.getLabelAt(i) == name) {
foundMenu = currMenu.items[i];
break;
}
}
if (foundMenu) {
result = foundMenu;
currMenu = foundMenu.submenu;
} else {
throw new Error(`Can not find item ${parentLabels.join(" -> ")}`);
}
}
return result;
};
Menu.setApplicationMenu(menu);
Inside tests:
await client.execute(() => {
var menu = electron.remote.Menu.getApplicationMenu();
menu.getItemByNames('Database', 'Create Database').click();
});
Is this working with https://www.npmjs.com/package/spectron-menu-addon ?
Hello, is anyone successfully used spectron-menu-addon
https://www.npmjs.com/package/spectron-menu-addon
using https://yarn.pm/spectron-menu-addon
I don't know if anyone still needs a way of right clicking in spectron but you can use the windows shortcut of Shift + F10 and the spectronKeys node module after you have clicked on the element you want. So this is an example that works for me.
return app.client .click(element).pause(1000) //click on desired element .keys(spectronKeys.mapAccelerator("Shift+F10")) .click(element of the desired option).pause(3000) .keys(spectronKeys.mapAccelerator("Shift")).pause(1000) // then at the end resend a key press of Shift in order to free the button.
@dimzaik It seems like a good alternative indeed for operating the menu. First send Control+F2 to focus the menu, then send 'File' keys to focus the 'File' menu, than send Return to execute the action.
Hello, is anyone successfully used spectron-menu-addon
https://www.npmjs.com/package/spectron-menu-addon
using https://yarn.pm/spectron-menu-addon
I got this little bit to work:
const spectronMenuAddon = require('spectron-menu-addon');
const menuAddon = new spectronMenuAddon.SpectronMenuAddon();
const electron = require('electron');
it('Can access menu', async () => {
const app = await menuAddon.createApplication({ args: ['./'], path: electron }).start();
menuAddon.clickMenu('Settings', 'Background');
await app.stop();
});
Hello, is anyone successfully used spectron-menu-addon https://www.npmjs.com/package/spectron-menu-addon using https://yarn.pm/spectron-menu-addon
I got this little bit to work:
const spectronMenuAddon = require('spectron-menu-addon'); const menuAddon = new spectronMenuAddon.SpectronMenuAddon(); const electron = require('electron'); it('Can access menu', async () => { const app = await menuAddon.createApplication({ args: ['./'], path: electron }).start(); menuAddon.clickMenu('Settings', 'Background'); await app.stop(); });
I recently published an update to spectron-menu-addon to deal with some Electron/Spectron updates. I don't have write permission to the original repo., so had to create spectron-menu-addon-v2. I also updated the example code to work with modern electron/spectron. Hope it helps folks.
I used https://github.com/joe-re/spectron-fake-menu, which seems to take an identical approach to the problem like https://www.npmjs.com/package/spectron-menu-addon, except the preload configuration gets injected after the app is created instead of having to create a SpectronMenuAddon
object instance.
I had to modify it slightly because I was using the focusedWindow in MenuItem.click(item, focusedWindow, event)
to send events to the renderer process, and the spectron-fake-menu
library didn't account for this.
Modifying the spectron-fake-menu/preload.js
script got it to work:
const {ipcMain, BrowserWindow, Menu} = require('electron');
function findItem(menuItem, labels) {
const target = labels[0];
const rest = labels.slice(1);
const foundItem = menuItem.find(item => item.label === target);
if (rest.length === 0) {
return foundItem;
}
return findItem(foundItem.submenu.items, rest);
}
const {ipcMain, BrowserWindow, Menu} = require('electron');
ipcMain.on('SPECTRON_FAKE_MENU/SEND', (_e, labels) => {
const item = findItem(Menu.getApplicationMenu().items, labels);
item.click(item, BrowserWindow.getFocusedWindow());
});
Here is the code that runs successfully after this tweak:
const spectron = require('spectron');
const fakeMenu = require('spectron-fake-menu');
const electron = require('electron');
it('Can access menu', async () => {
const app = new spectron.Application({ args: ['./'], path: electron });
fakeMenu.apply(app);
await app.start();
await app.client.waitUntilWindowLoaded();
fakeMenu.clickMenu('File, 'Open');
// assert that the open() menu is called
await app.stop();
});
Is this working with https://www.npmjs.com/package/spectron-menu-addon ?
No its not, I tried it with electron 11.4.5 and spectron 13.0.0. If you go inside the code https://github.com/jdhiser/spectron-menu-addon/blob/master/src/index.ts#L32 you will see that app.electron.ipcRenderer
is no longer a valid API, so it will throw Cannot read property 'ipcRenderer'
error.