e03a40026a
Fixes #18808 Previously, the triggeredByAccelerator flag would be entirely coupled with whether or not the modifier keys were being used or not. This PR swaps out the ui::EventFlagsFromModifiers([event modifierFlags])) call in the macOS code to ui::EventFlagsFromNSEventWithModifiers(event, [event modifierFlags])). The latter outputs flags that take into account mouse click events on top of modifier flags (see Chromium documentation). The business logic to detect triggeredByAccelerator is then changed to exclude any mouse click flags.
932 lines
24 KiB
JavaScript
932 lines
24 KiB
JavaScript
const chai = require('chai')
|
|
const dirtyChai = require('dirty-chai')
|
|
|
|
const { ipcRenderer, remote } = require('electron')
|
|
const { BrowserWindow, globalShortcut, Menu, MenuItem } = remote
|
|
const { sortMenuItems } = require('../lib/browser/api/menu-utils')
|
|
const { closeWindow } = require('./window-helpers')
|
|
|
|
const { expect } = chai
|
|
chai.use(dirtyChai)
|
|
|
|
const isCi = remote.getGlobal('isCi')
|
|
|
|
describe('Menu module', () => {
|
|
describe('Menu.buildFromTemplate', () => {
|
|
it('should be able to attach extra fields', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{
|
|
label: 'text',
|
|
extra: 'field'
|
|
}
|
|
])
|
|
expect(menu.items[0].extra).to.equal('field')
|
|
})
|
|
|
|
it('should be able to accept only MenuItems', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
new MenuItem({ label: 'one' }),
|
|
new MenuItem({ label: 'two' })
|
|
])
|
|
expect(menu.items[0].label).to.equal('one')
|
|
expect(menu.items[1].label).to.equal('two')
|
|
})
|
|
|
|
it('should be able to accept only MenuItems in a submenu', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{
|
|
label: 'one',
|
|
submenu: [
|
|
new MenuItem({ label: 'two' })
|
|
]
|
|
}
|
|
])
|
|
expect(menu.items[0].label).to.equal('one')
|
|
expect(menu.items[0].submenu.items[0].label).to.equal('two')
|
|
})
|
|
|
|
it('should be able to accept MenuItems and plain objects', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
new MenuItem({ label: 'one' }),
|
|
{ label: 'two' }
|
|
])
|
|
expect(menu.items[0].label).to.equal('one')
|
|
expect(menu.items[1].label).to.equal('two')
|
|
})
|
|
|
|
it('does not modify the specified template', () => {
|
|
const template = [{ label: 'text', submenu: [{ label: 'sub' }] }]
|
|
const result = ipcRenderer.sendSync('eval', `const template = [{label: 'text', submenu: [{label: 'sub'}]}]\nrequire('electron').Menu.buildFromTemplate(template)\ntemplate`)
|
|
expect(result).to.deep.equal(template)
|
|
})
|
|
|
|
it('does not throw exceptions for undefined/null values', () => {
|
|
expect(() => {
|
|
Menu.buildFromTemplate([
|
|
{
|
|
label: 'text',
|
|
accelerator: undefined
|
|
},
|
|
{
|
|
label: 'text again',
|
|
accelerator: null
|
|
}
|
|
])
|
|
}).to.not.throw()
|
|
})
|
|
|
|
it('does throw exceptions for empty objects and null values', () => {
|
|
expect(() => {
|
|
Menu.buildFromTemplate([{}, null])
|
|
}).to.throw(/Invalid template for MenuItem: must have at least one of label, role or type/)
|
|
})
|
|
|
|
it('does throw exception for object without role, label, or type attribute', () => {
|
|
expect(() => {
|
|
Menu.buildFromTemplate([{ 'visible': true }])
|
|
}).to.throw(/Invalid template for MenuItem: must have at least one of label, role or type/)
|
|
})
|
|
it('does throw exception for undefined', () => {
|
|
expect(() => {
|
|
Menu.buildFromTemplate([undefined])
|
|
}).to.throw(/Invalid template for MenuItem: must have at least one of label, role or type/)
|
|
})
|
|
|
|
describe('Menu sorting and building', () => {
|
|
describe('sorts groups', () => {
|
|
it('does a simple sort', () => {
|
|
const items = [
|
|
{
|
|
label: 'two',
|
|
id: '2',
|
|
afterGroupContaining: ['1'] },
|
|
{ type: 'separator' },
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
}
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two',
|
|
afterGroupContaining: ['1']
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
|
|
it('does a simple sort with MenuItems', () => {
|
|
const firstItem = new MenuItem({ id: '1', label: 'one' })
|
|
const secondItem = new MenuItem({
|
|
label: 'two',
|
|
id: '2',
|
|
afterGroupContaining: ['1']
|
|
})
|
|
const sep = new MenuItem({ type: 'separator' })
|
|
|
|
const items = [ secondItem, sep, firstItem ]
|
|
const expected = [ firstItem, sep, secondItem ]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
|
|
it('resolves cycles by ignoring things that conflict', () => {
|
|
const items = [
|
|
{
|
|
id: '2',
|
|
label: 'two',
|
|
afterGroupContaining: ['1']
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '1',
|
|
label: 'one',
|
|
afterGroupContaining: ['2']
|
|
}
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '1',
|
|
label: 'one',
|
|
afterGroupContaining: ['2']
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two',
|
|
afterGroupContaining: ['1']
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
|
|
it('ignores references to commands that do not exist', () => {
|
|
const items = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two',
|
|
afterGroupContaining: ['does-not-exist']
|
|
}
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two',
|
|
afterGroupContaining: ['does-not-exist']
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
|
|
it('only respects the first matching [before|after]GroupContaining rule in a given group', () => {
|
|
const items = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
beforeGroupContaining: ['1']
|
|
},
|
|
{
|
|
id: '4',
|
|
label: 'four',
|
|
afterGroupContaining: ['2']
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
}
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
beforeGroupContaining: ['1']
|
|
},
|
|
{
|
|
id: '4',
|
|
label: 'four',
|
|
afterGroupContaining: ['2']
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
})
|
|
|
|
describe('moves an item to a different group by merging groups', () => {
|
|
it('can move a group of one item', () => {
|
|
const items = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['1']
|
|
},
|
|
{ type: 'separator' }
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['1']
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
|
|
it("moves all items in the moving item's group", () => {
|
|
const items = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['1']
|
|
},
|
|
{
|
|
id: '4',
|
|
label: 'four'
|
|
},
|
|
{ type: 'separator' }
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['1']
|
|
},
|
|
{
|
|
id: '4',
|
|
label: 'four'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
|
|
it("ignores positions relative to commands that don't exist", () => {
|
|
const items = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['does-not-exist']
|
|
},
|
|
{
|
|
id: '4',
|
|
label: 'four',
|
|
after: ['1']
|
|
},
|
|
{ type: 'separator' }
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['does-not-exist']
|
|
},
|
|
{
|
|
id: '4',
|
|
label: 'four',
|
|
after: ['1']
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
|
|
it('can handle recursive group merging', () => {
|
|
const items = [
|
|
{
|
|
id: '1',
|
|
label: 'one',
|
|
after: ['3']
|
|
},
|
|
{
|
|
id: '2',
|
|
label: 'two',
|
|
before: ['1']
|
|
},
|
|
{
|
|
id: '3',
|
|
label: 'three'
|
|
}
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '3',
|
|
label: 'three'
|
|
},
|
|
{
|
|
id: '2',
|
|
label: 'two',
|
|
before: ['1']
|
|
},
|
|
{
|
|
id: '1',
|
|
label: 'one',
|
|
after: ['3']
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
|
|
it('can merge multiple groups when given a list of before/after commands', () => {
|
|
const items = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['1', '2']
|
|
}
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
},
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['1', '2']
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
|
|
it('can merge multiple groups based on both before/after commands', () => {
|
|
const items = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['1'],
|
|
before: ['2']
|
|
}
|
|
]
|
|
|
|
const expected = [
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
},
|
|
{
|
|
id: '3',
|
|
label: 'three',
|
|
after: ['1'],
|
|
before: ['2']
|
|
},
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
}
|
|
]
|
|
|
|
expect(sortMenuItems(items)).to.deep.equal(expected)
|
|
})
|
|
})
|
|
|
|
it('should position before existing item', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
}, {
|
|
id: '3',
|
|
label: 'three'
|
|
}, {
|
|
id: '1',
|
|
label: 'one',
|
|
before: ['2']
|
|
}
|
|
])
|
|
|
|
expect(menu.items[0].label).to.equal('one')
|
|
expect(menu.items[1].label).to.equal('two')
|
|
expect(menu.items[2].label).to.equal('three')
|
|
})
|
|
|
|
it('should position after existing item', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{
|
|
id: '2',
|
|
label: 'two',
|
|
after: ['1']
|
|
},
|
|
{
|
|
id: '1',
|
|
label: 'one'
|
|
}, {
|
|
id: '3',
|
|
label: 'three'
|
|
}
|
|
])
|
|
|
|
expect(menu.items[0].label).to.equal('one')
|
|
expect(menu.items[1].label).to.equal('two')
|
|
expect(menu.items[2].label).to.equal('three')
|
|
})
|
|
|
|
it('should filter excess menu separators', () => {
|
|
const menuOne = Menu.buildFromTemplate([
|
|
{
|
|
type: 'separator'
|
|
}, {
|
|
label: 'a'
|
|
}, {
|
|
label: 'b'
|
|
}, {
|
|
label: 'c'
|
|
}, {
|
|
type: 'separator'
|
|
}
|
|
])
|
|
|
|
expect(menuOne.items).to.have.length(3)
|
|
expect(menuOne.items[0].label).to.equal('a')
|
|
expect(menuOne.items[1].label).to.equal('b')
|
|
expect(menuOne.items[2].label).to.equal('c')
|
|
|
|
const menuTwo = Menu.buildFromTemplate([
|
|
{
|
|
type: 'separator'
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: 'a'
|
|
}, {
|
|
label: 'b'
|
|
}, {
|
|
label: 'c'
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
type: 'separator'
|
|
}
|
|
])
|
|
|
|
expect(menuTwo.items).to.have.length(3)
|
|
expect(menuTwo.items[0].label).to.equal('a')
|
|
expect(menuTwo.items[1].label).to.equal('b')
|
|
expect(menuTwo.items[2].label).to.equal('c')
|
|
})
|
|
|
|
it('should continue inserting items at next index when no specifier is present', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{
|
|
id: '2',
|
|
label: 'two'
|
|
}, {
|
|
id: '3',
|
|
label: 'three'
|
|
}, {
|
|
id: '4',
|
|
label: 'four'
|
|
}, {
|
|
id: '5',
|
|
label: 'five'
|
|
}, {
|
|
id: '1',
|
|
label: 'one',
|
|
before: ['2']
|
|
}
|
|
])
|
|
|
|
expect(menu.items[0].label).to.equal('one')
|
|
expect(menu.items[1].label).to.equal('two')
|
|
expect(menu.items[2].label).to.equal('three')
|
|
expect(menu.items[3].label).to.equal('four')
|
|
expect(menu.items[4].label).to.equal('five')
|
|
})
|
|
|
|
it('should continue inserting MenuItems at next index when no specifier is present', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
new MenuItem({
|
|
id: '2',
|
|
label: 'two'
|
|
}), new MenuItem({
|
|
id: '3',
|
|
label: 'three'
|
|
}), new MenuItem({
|
|
id: '4',
|
|
label: 'four'
|
|
}), new MenuItem({
|
|
id: '5',
|
|
label: 'five'
|
|
}), new MenuItem({
|
|
id: '1',
|
|
label: 'one',
|
|
before: ['2']
|
|
})
|
|
])
|
|
|
|
expect(menu.items[0].label).to.equal('one')
|
|
expect(menu.items[1].label).to.equal('two')
|
|
expect(menu.items[2].label).to.equal('three')
|
|
expect(menu.items[3].label).to.equal('four')
|
|
expect(menu.items[4].label).to.equal('five')
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('Menu.getMenuItemById', () => {
|
|
it('should return the item with the given id', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{
|
|
label: 'View',
|
|
submenu: [
|
|
{
|
|
label: 'Enter Fullscreen',
|
|
accelerator: 'ControlCommandF',
|
|
id: 'fullScreen'
|
|
}
|
|
]
|
|
}
|
|
])
|
|
const fsc = menu.getMenuItemById('fullScreen')
|
|
expect(menu.items[0].submenu.items[0]).to.equal(fsc)
|
|
})
|
|
|
|
it('should return the separator with the given id', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{
|
|
label: 'Item 1',
|
|
id: 'item_1'
|
|
},
|
|
{
|
|
id: 'separator',
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: 'Item 2',
|
|
id: 'item_2'
|
|
}
|
|
])
|
|
const separator = menu.getMenuItemById('separator')
|
|
expect(separator).to.be.an('object')
|
|
expect(separator).to.equal(menu.items[1])
|
|
})
|
|
})
|
|
|
|
describe('Menu.insert', () => {
|
|
it('should throw when attempting to insert at out-of-range indices', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{ label: '1' },
|
|
{ label: '2' },
|
|
{ label: '3' }
|
|
])
|
|
|
|
const item = new MenuItem({ label: 'badInsert' })
|
|
|
|
expect(() => {
|
|
menu.insert(9999, item)
|
|
}).to.throw(/Position 9999 cannot be greater than the total MenuItem count/)
|
|
|
|
expect(() => {
|
|
menu.insert(-9999, item)
|
|
}).to.throw(/Position -9999 cannot be less than 0/)
|
|
})
|
|
|
|
it('should store item in @items by its index', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{ label: '1' },
|
|
{ label: '2' },
|
|
{ label: '3' }
|
|
])
|
|
|
|
const item = new MenuItem({ label: 'inserted' })
|
|
|
|
menu.insert(1, item)
|
|
expect(menu.items[0].label).to.equal('1')
|
|
expect(menu.items[1].label).to.equal('inserted')
|
|
expect(menu.items[2].label).to.equal('2')
|
|
expect(menu.items[3].label).to.equal('3')
|
|
})
|
|
})
|
|
|
|
describe('Menu.append', () => {
|
|
it('should add the item to the end of the menu', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{ label: '1' },
|
|
{ label: '2' },
|
|
{ label: '3' }
|
|
])
|
|
|
|
const item = new MenuItem({ label: 'inserted' })
|
|
menu.append(item)
|
|
|
|
expect(menu.items[0].label).to.equal('1')
|
|
expect(menu.items[1].label).to.equal('2')
|
|
expect(menu.items[2].label).to.equal('3')
|
|
expect(menu.items[3].label).to.equal('inserted')
|
|
})
|
|
})
|
|
|
|
describe('Menu.popup', () => {
|
|
let w = null
|
|
let menu
|
|
|
|
beforeEach(() => {
|
|
w = new BrowserWindow({ show: false, width: 200, height: 200 })
|
|
menu = Menu.buildFromTemplate([
|
|
{ label: '1' },
|
|
{ label: '2' },
|
|
{ label: '3' }
|
|
])
|
|
})
|
|
|
|
afterEach(() => {
|
|
menu.closePopup()
|
|
menu.closePopup(w)
|
|
return closeWindow(w).then(() => { w = null })
|
|
})
|
|
|
|
it('throws an error if options is not an object', () => {
|
|
expect(() => {
|
|
menu.popup('this is a string, not an object')
|
|
}).to.throw(/Options must be an object/)
|
|
})
|
|
|
|
it('allows for options to be optional', () => {
|
|
expect(() => {
|
|
menu.popup({})
|
|
}).to.not.throw()
|
|
})
|
|
|
|
it('should emit menu-will-show event', (done) => {
|
|
menu.on('menu-will-show', () => { done() })
|
|
menu.popup({ window: w })
|
|
})
|
|
|
|
it('should emit menu-will-close event', (done) => {
|
|
menu.on('menu-will-close', () => { done() })
|
|
menu.popup({ window: w })
|
|
menu.closePopup()
|
|
})
|
|
|
|
it('returns immediately', () => {
|
|
const input = { window: w, x: 100, y: 101 }
|
|
const output = menu.popup(input)
|
|
expect(output.x).to.equal(input.x)
|
|
expect(output.y).to.equal(input.y)
|
|
expect(output.browserWindow).to.equal(input.window)
|
|
})
|
|
|
|
it('works without a given BrowserWindow and options', () => {
|
|
const { browserWindow, x, y } = menu.popup({ x: 100, y: 101 })
|
|
|
|
expect(browserWindow.constructor.name).to.equal('BrowserWindow')
|
|
expect(x).to.equal(100)
|
|
expect(y).to.equal(101)
|
|
})
|
|
|
|
it('works with a given BrowserWindow, options and callback', (done) => {
|
|
const { x, y } = menu.popup({
|
|
window: w,
|
|
x: 100,
|
|
y: 101,
|
|
callback: () => done()
|
|
})
|
|
|
|
expect(x).to.equal(100)
|
|
expect(y).to.equal(101)
|
|
menu.closePopup()
|
|
})
|
|
|
|
it('works with a given BrowserWindow, no options, and a callback', (done) => {
|
|
menu.popup({ window: w, callback: () => done() })
|
|
menu.closePopup()
|
|
})
|
|
})
|
|
|
|
describe('Menu.setApplicationMenu', () => {
|
|
it('sets a menu', () => {
|
|
const menu = Menu.buildFromTemplate([
|
|
{ label: '1' },
|
|
{ label: '2' }
|
|
])
|
|
|
|
Menu.setApplicationMenu(menu)
|
|
expect(Menu.getApplicationMenu()).to.not.be.null()
|
|
})
|
|
|
|
it('unsets a menu with null', () => {
|
|
Menu.setApplicationMenu(null)
|
|
expect(Menu.getApplicationMenu()).to.be.null()
|
|
})
|
|
})
|
|
|
|
describe('menu accelerators', async () => {
|
|
const sendRobotjsKey = (key, modifiers = [], delay = 500) => {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
require('robotjs').keyTap(key, modifiers)
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, delay)
|
|
} catch (e) {
|
|
reject(e)
|
|
}
|
|
})
|
|
}
|
|
|
|
before(async function () {
|
|
// --ci flag breaks accelerator and robotjs interaction
|
|
if (isCi) {
|
|
this.skip()
|
|
}
|
|
|
|
// before accelerator tests, use globalShortcut to test if
|
|
// RobotJS is working at all
|
|
let isKeyPressed = false
|
|
globalShortcut.register('q', () => {
|
|
isKeyPressed = true
|
|
})
|
|
try {
|
|
await sendRobotjsKey('q')
|
|
} catch (e) {
|
|
this.skip()
|
|
}
|
|
|
|
if (!isKeyPressed) {
|
|
this.skip()
|
|
}
|
|
|
|
globalShortcut.unregister('q')
|
|
})
|
|
|
|
it('should perform the specified action', async () => {
|
|
let hasBeenClicked = false
|
|
const menu = Menu.buildFromTemplate([
|
|
{
|
|
label: 'Test',
|
|
submenu: [
|
|
{
|
|
label: 'Test Item',
|
|
accelerator: 'T',
|
|
click: (a, b, event) => {
|
|
hasBeenClicked = true
|
|
expect(event).to.deep.equal({
|
|
shiftKey: false,
|
|
ctrlKey: false,
|
|
altKey: false,
|
|
metaKey: false,
|
|
triggeredByAccelerator: true
|
|
})
|
|
},
|
|
id: 'test'
|
|
}
|
|
]
|
|
}
|
|
])
|
|
Menu.setApplicationMenu(menu)
|
|
expect(Menu.getApplicationMenu()).to.not.be.null()
|
|
await sendRobotjsKey('t')
|
|
expect(hasBeenClicked).to.equal(true)
|
|
})
|
|
|
|
it('should not activate upon clicking another key combination', async () => {
|
|
let hasBeenClicked = false
|
|
const menu = Menu.buildFromTemplate([
|
|
{
|
|
label: 'Test',
|
|
submenu: [
|
|
{
|
|
label: 'Test Item',
|
|
accelerator: 'T',
|
|
click: (a, b, event) => {
|
|
hasBeenClicked = true
|
|
},
|
|
id: 'test'
|
|
}
|
|
]
|
|
}
|
|
])
|
|
Menu.setApplicationMenu(menu)
|
|
expect(Menu.getApplicationMenu()).to.not.be.null()
|
|
await sendRobotjsKey('t', 'shift')
|
|
expect(hasBeenClicked).to.equal(false)
|
|
})
|
|
})
|
|
})
|