Commit 896e8ec8 896e8ec837da78b6a76a57d912d3cb3c60adb1ff by Nicolas Perriault

added support for key modifiers to Casper#sendKeys()

1 parent c528b220
......@@ -3,7 +3,7 @@ branches:
- "master"
- "1.0"
before_script:
- "npm install -g jshint@1.0.0"
- "npm install -g jshint@1.1.0"
- "phantomjs --version"
- "export PHANTOMJS_EXECUTABLE='phantomjs --local-to-remote-url-access=yes --ignore-ssl-errors=yes'"
script:
......
......@@ -116,6 +116,7 @@ Last, all the casper test suites have been upgraded to use the new testing featu
- fixed [#441](https://github.com/n1k0/casperjs/issues/441) - added `--ssl-protocol` option support to the `casperjs` executable
- Added [`Casper#fillSelectors()`](http://docs.casperjs.org/en/latest/modules/casper.html#fillselectors) and [`Casper#fillXPath()`](http://docs.casperjs.org/en/latest/modules/casper.html#fillxpath)
- Added [`Casper#getElementsAttribute()`](http://docs.casperjs.org/en/latest/modules/casper.html#getelementsattribute) and [`Casper#getElementsInfo()`](http://docs.casperjs.org/en/latest/modules/casper.html#getelementsinfo)
- Added support for key modifiers to `Casper#sendKeys()`
- `cli`: Now dropping an arg or an option will be reflected in their *raw* equivalent
- `cli.get()` now supports fallback values
......
......@@ -1419,12 +1419,37 @@ Sends native keyboard events to the element matching the provided :doc:`selector
this.click('form.contact input[type="submit"]');
});
Note that ``sendKeys()`` by default will remove the focus on text input fields, which will typically close autocomplete widgets. If you want to maintain focus, use the ``keepFocus`` option. For example, if using jQuery-UI, you can click on the first autocomplete suggestion using::
.. versionadded:: 1.1
Options
~~~~~~~
- ``(Boolean) keepFocus``:
``sendKeys()`` by default will remove the focus on text input fields, which will typically close autocomplete widgets. If you want to maintain focus, us e the ``keepFocus`` option. For example, if using jQuery-UI, you can click on the first autocomplete suggestion using::
casper.then(function() {
this.sendKeys('form.contact input#name', 'action', {keepFocus: true});
this.click('form.contact ul.ui-autocomplete li.ui-menu-item:first- child a');
});
- ``(String) modifiers``:
``sendKeys()`` accepts a ``modifiers`` option to support key modifiers. The options is a string representing the composition of modifiers to use, separated by the ``+`` character::
casper.then(function() {
this.sendKeys('document', 's', {modifiers: 'ctrl+alt+shift'});
});
Available modifiers are:
- ``ctrl``
- ``alt``
- ``shift``
- ``meta``
- ``keypad``
casper.then(function() {
this.sendKeys('form.contact input#name', 'action', {keepFocus: true});
this.click('form.contact ul.ui-autocomplete li.ui-menu-item:first-child a');
});
.. index:: auth
......
......@@ -965,7 +965,7 @@ Casper.prototype.getElementAttr = function getElementAttr(selector, attribute) {
/**
* Retrieves the value of an attribute for each element matching the provided
* DOM CSS3/XPath selector.
*
*
* @param String selector A DOM CSS3/XPath selector
* @param String attribute The attribute name to lookup
* @return Array
......@@ -1531,6 +1531,11 @@ Casper.prototype.runStep = function runStep(step) {
/**
* Sends keys to given element.
*
* Options:
*
* - eventType: "keypress", "keyup" or "keydown" (default: "keypress")
* - modifiers: a string defining the key modifiers, eg. "alt", "alt+shift"
*
* @param String selector A DOM CSS3 compatible selector
* @param String keys A string representing the sequence of char codes to send
* @param Object options Options
......@@ -1543,18 +1548,20 @@ Casper.prototype.sendKeys = function(selector, keys, options) {
eventType: 'keypress'
}, options || {});
var elemInfos = this.getElementInfo(selector),
tag = elemInfos.nodeName.toLowerCase(),
type = utils.getPropertyPath(elemInfos, 'attributes.type'),
supported = ["color", "date", "datetime", "datetime-local", "email",
"hidden", "month", "number", "password", "range", "search",
"tel", "text", "time", "url", "week"];
var isTextInput = false;
tag = elemInfos.nodeName.toLowerCase(),
type = utils.getPropertyPath(elemInfos, 'attributes.type'),
supported = ["color", "date", "datetime", "datetime-local", "email",
"hidden", "month", "number", "password", "range", "search",
"tel", "text", "time", "url", "week"],
isTextInput = false;
if (tag === 'textarea' || (tag === 'input' && supported.indexOf(type) !== -1)) {
// clicking on the input element brings it focus
isTextInput = true;
this.click(selector);
}
this.page.sendEvent(options.eventType, keys);
var modifiers = utils.computeModifier(options && options.modifiers,
this.page.event.modifier)
this.page.sendEvent(options.eventType, keys, null, null, modifiers);
if (isTextInput && !options.keepFocus) {
// remove the focus
this.evaluate(function(selector) {
......
......@@ -90,6 +90,30 @@ function clone(o) {
exports.clone = clone;
/**
* Computes a modifier string to its PhantomJS equivalent. A modifier string is
* in the form "ctrl+alt+shift".
*
* @param String modifierString Modifier string, eg. "ctrl+alt+shift"
* @param Object modifiers Modifiers definitions
* @return Number
*/
function computeModifier(modifierString, modifiers) {
"use strict";
var modifier = 0,
checkKey = function(key) {
if (key in modifiers) return;
throw new CasperError(format('%s is not a supported key modifier', key));
};
if (!modifierString) return modifier;
var keys = modifierString.split('+');
keys.forEach(checkKey);
return keys.reduce(function(acc, key) {
return acc | modifiers[key];
}, modifier);
}
exports.computeModifier = computeModifier;
/**
* Dumps a JSON representation of passed value to the console. Used for
* debugging purpose only.
*
......
......@@ -17,3 +17,25 @@ casper.test.begin('sendKeys() tests', 3, function(test) {
test.done();
});
});
casper.test.begin('sendKeys() key modifiers tests', 1, function(test) {
casper.start().then(function() {
this.setContent([
'<input>',
'<script>var keys = []; window.addEventListener("keypress", function(e) {',
' keys.push({code: e.which, alt: e.altKey, ctrl: e.ctrlKey});',
'})</script>'
].join(''));
this.sendKeys('input', 'k');
this.sendKeys('input', 'k', {modifiers: "ctrl"});
this.sendKeys('input', 'k', {modifiers: "ctrl+alt"});
test.assertEquals(this.getGlobal('keys'),
[
{code: 107, alt: false, ctrl: false},
{code: 107, alt: false, ctrl: true},
{code: 107, alt: true, ctrl: true}
], 'sendKeys() uses key modifiers');
}).run(function() {
test.done();
});
});
......
......@@ -52,6 +52,27 @@ casper.test.begin('utils.clone() tests', 2, function(test) {
test.done();
});
casper.test.begin('utils.computeModifier() tests', 7, function(test) {
var modifiers = require('webpage').create().event.modifier;
test.assertType(modifiers, "object");
test.assertEquals(utils.computeModifier("", modifiers), 0,
'computeModifier() computes a "none" modifier');
test.assertEquals(utils.computeModifier("alt", modifiers),
modifiers.alt,
'computeModifier() computes an "alt" modifier');
test.assertEquals(utils.computeModifier("ctrl+alt", modifiers),
modifiers.ctrl | modifiers.alt,
'computeModifier() computes a "ctrl+alt" modifier');
test.assertEquals(utils.computeModifier("ctrl+alt+shift", modifiers),
modifiers.ctrl | modifiers.alt | modifiers.shift,
'computeModifier() computes a "ctrl+alt+shift" modifier');
test.assertThrows(utils.computeModifier, ["chucknorris", modifiers],
'computeModifier() checks for a valid modifier');
test.assertThrows(utils.computeModifier, ["chuck+norris", modifiers],
'computeModifier() checks for a valid complex modifier');
test.done();
});
casper.test.begin('equals() tests', 23, function(test) {
test.assert(utils.equals(null, null), 'equals() null equality');
test.assertNot(utils.equals(null, undefined), 'equals() null vs. undefined inequality');
......