Closed nickforddev closed 7 years ago
Hm, the only way I can think of off the top of my head to do this is with an accelerator. Accelerators let you send keys like the arrow keys. Out of the box, I don't think Nightmare supports accelerators, but I think you could write something with .action()
and webview.sendInputEvent()
to accomplish what you're after.
To help others who also need this, I managed to get it working without too much trouble using Ross's advice:
Nightmare.action('specialKeyPress',
function(name, options, parent, win, renderer, done) {
parent.respondTo('specialKeyPress', async function(keyCode, done) {
// See this: https://github.com/electron/electron/blob/master/docs/api/web-contents.md#contentssendinputeventevent
// and this: https://github.com/electron/electron/blob/master/docs/api/accelerator.md
win.focus();
win.webContents.sendInputEvent({ type:'keyDown', keyCode });
await new Promise(resolve => setTimeout(resolve, 50));
win.webContents.sendInputEvent({ type:'keyUp', keyCode });
done();
});
done();
},
function(keyCode, done) {
this.child.call('specialKeyPress', keyCode, done);
});
It takes any of the keyCode
s listed in this document. Use it like this:
await nightmare
.goto('http://blahblah.com')
.specialKeyPress('Down'); // or `Up` or `Right` etc.
Note that you don't need this custom action for Enter
and Tab
, which can be triggered with .type('body','\u000d')
and .type('body','\u0009')
, respectively.
Looks like this is resolved. I am going to close this for now but feel free to open a new issue if you are still having problems.
This isn't working for me. I'm trying to use it to backspace pre-existing text in an input box. I also tried sending PrintScreen as a test just to see if the action is working but that didn't work either:
const Nightmare = require('nightmare');
const vo = require('vo');
Nightmare.action('specialKeyPress',
function(name, options, parent, win, renderer, done) {
parent.respondTo('specialKeyPress', async function(keyCode, done) {
// See this: https://github.com/electron/electron/blob/master/docs/api/web-contents.md#contentssendinputeventevent
// and this: https://github.com/electron/electron/blob/master/docs/api/accelerator.md
win.focus();
win.webContents.sendInputEvent({ type:'keyDown', keyCode });
await new Promise(resolve => setTimeout(resolve, 50));
win.webContents.sendInputEvent({ type:'keyUp', keyCode });
done();
});
done();
},
function(keyCode, done) {
this.child.call('specialKeyPress', keyCode, done);
});
var run = function * () {
const nightmare = Nightmare({
show: true,
switches: {
/////'proxy-server': 'http://' + proxyHost + ':' + proxyPort,
'ignore-certificate-errors': true
},
waitTimeout: 90000,
gotoTimeout: 90000
});
yield nightmare.goto('https://www.google.com');
yield nightmare.click('div.innerWrap input[aria-haspopup="true"]'); // Click city input
yield nightmare.specialKeyPress('PrintScreen');
for(i = 0; i < 40; i++){
yield nightmare.specialKeyPress('Right'); // Move cursor right of any text
}
for(i = 0; i < 40; i++){
yield nightmare.type('div.innerWrap input[aria-haspopup="true"]','\u0008'); // Backspace city input
}
yield nightmare.end(() => "some value");
yield nightmare.then(function () {
console.log("ended");
//////xvfb.stopSync();
});
}
vo(run)(function(err, titles) {
console.dir(titles);
});
For anyone coming back to this, the above solution no longer works, instead you should use the Press
custom action in this runnable example:
const Nightmare = require('nightmare')
main().catch(console.error)
async function main() {
Press(Nightmare)
const nightmare = Nightmare({ show: true })
await nightmare.goto('https://google.com')
await nightmare.type('#lst-ib', 'nightmare is cool')
await nightmare.press('#lst-ib', 'Backspace')
await nightmare.press('#lst-ib', 'Backspace')
await nightmare.press('#lst-ib', 'Backspace')
await nightmare.press('#lst-ib', 'Backspace')
await nightmare.type('#lst-ib', 'awesome!')
await sleep(5000)
await nightmare.end()
}
function sleep(ms) {
return new Promise(res => setTimeout(res, ms))
}
function Press(Nightmare) {
Nightmare.action(
'press',
function(name, options, parent, win, renderer, done) {
parent.respondTo('press', function(keyCode, done) {
win.webContents.sendInputEvent({ type: 'keyDown', keyCode: keyCode })
win.webContents.sendInputEvent({ type: 'keyUp', keyCode: keyCode })
done()
})
done()
},
function(selector, keyCode, done) {
// focus, press, blur
// TODO: clean me up
return this.evaluate_now(
selector => document.querySelector(selector).focus(),
() =>
this.child.call('press', keyCode, () => {
this.evaluate_now(
selector => document.querySelector(selector).blur(),
() => done(),
selector
)
}),
selector
)
}
)
}
@matthewmueller 's solution works fine. Thanks.
This would be a nice feature in NightmareJS!
I've seen several threads and issues about mimicking a user hitting the enter or delete keys, but can't find anything about arrow keys. I've already tried converting the keycode values to hex ie.
40
, which is the down arrow on a Mac, would be\u0028
I suppose, but that is(
. I tried every variation I could find, including a hex value I found in some Apple native API, can't find anything that works for arrow keys in Nightmare.js.So, is this even possible?
Also, is this still the way to go about it? This actually returns an error:
Cannot read property 'focus' of null