electron-userland / spectron

DEPRECATED: 🔎 Test Electron apps using ChromeDriver
http://electronjs.org/spectron
MIT License
1.68k stars 229 forks source link

Testing menu interaction #21

Open atdrago opened 8 years ago

atdrago commented 8 years ago

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!

kevinsawicki commented 8 years ago

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 👍

navinjoy commented 8 years ago

@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.

atdrago commented 8 years ago

@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

  1. Inject some ipcMain listeners when NODE_ENV=development https://github.com/atdrago/negative/blob/develop/lib/negative.js#L47-L49
  2. The listeners call a simulateMenuClick function https://github.com/atdrago/negative/blob/develop/lib/negative-test.js#L35-L72
  3. simulateMenuClick checks if the menuItem is enabled and visible, and then calls its click handler if it has one https://github.com/atdrago/negative/blob/develop/lib/negative-test.js#L27-L29

In the tests

  1. 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

atdrago commented 8 years ago

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

navinjoy commented 8 years ago

@atdrago Thanks a lot for sharing this... we will try and let you know how it goes....have a great day 👍

kedunuru commented 8 years ago

thanks adam ...will try this and let u know how it goes ...this totally works !!! big thank you to adam !

atdrago commented 8 years ago

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?

kevinsawicki commented 8 years ago

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.

greenmanspirit commented 7 years ago

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!

devinaction commented 7 years ago

In Ember acceptance tests, I want to click a customized menu option. Is it possible? How do I do that?

jdhananjaya commented 7 years ago

I am trying to test my electron app - menu functionality using spectron and I wanted to know if there is any update...

Thanks

kontrollanten commented 7 years ago

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();
  });
ulfryk commented 6 years ago

Any updates on this one ?

Paxa commented 6 years ago

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();
    });
CapOM commented 5 years ago

Is this working with https://www.npmjs.com/package/spectron-menu-addon ?

SunithaGarikapati commented 5 years ago

Hello, is anyone successfully used spectron-menu-addon

https://www.npmjs.com/package/spectron-menu-addon

using https://yarn.pm/spectron-menu-addon

dimzaik commented 5 years ago

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.

bsivanov commented 4 years ago

@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.

dhong9 commented 4 years ago

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();
  });
jdhiser commented 4 years ago

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.

saxelsen commented 3 years ago

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();
  });
coda-nsit commented 3 years ago

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.