Commit 896e8ec8 896e8ec837da78b6a76a57d912d3cb3c60adb1ff by Nicolas Perriault

added support for key modifiers to Casper#sendKeys()

1 parent c528b220
...@@ -3,7 +3,7 @@ branches: ...@@ -3,7 +3,7 @@ branches:
3 - "master" 3 - "master"
4 - "1.0" 4 - "1.0"
5 before_script: 5 before_script:
6 - "npm install -g jshint@1.0.0" 6 - "npm install -g jshint@1.1.0"
7 - "phantomjs --version" 7 - "phantomjs --version"
8 - "export PHANTOMJS_EXECUTABLE='phantomjs --local-to-remote-url-access=yes --ignore-ssl-errors=yes'" 8 - "export PHANTOMJS_EXECUTABLE='phantomjs --local-to-remote-url-access=yes --ignore-ssl-errors=yes'"
9 script: 9 script:
......
...@@ -116,6 +116,7 @@ Last, all the casper test suites have been upgraded to use the new testing featu ...@@ -116,6 +116,7 @@ Last, all the casper test suites have been upgraded to use the new testing featu
116 - fixed [#441](https://github.com/n1k0/casperjs/issues/441) - added `--ssl-protocol` option support to the `casperjs` executable 116 - fixed [#441](https://github.com/n1k0/casperjs/issues/441) - added `--ssl-protocol` option support to the `casperjs` executable
117 - 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) 117 - 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)
118 - 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) 118 - 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)
119 - Added support for key modifiers to `Casper#sendKeys()`
119 - `cli`: Now dropping an arg or an option will be reflected in their *raw* equivalent 120 - `cli`: Now dropping an arg or an option will be reflected in their *raw* equivalent
120 - `cli.get()` now supports fallback values 121 - `cli.get()` now supports fallback values
121 122
......
...@@ -1419,12 +1419,37 @@ Sends native keyboard events to the element matching the provided :doc:`selector ...@@ -1419,12 +1419,37 @@ Sends native keyboard events to the element matching the provided :doc:`selector
1419 this.click('form.contact input[type="submit"]'); 1419 this.click('form.contact input[type="submit"]');
1420 }); 1420 });
1421 1421
1422 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:: 1422 .. versionadded:: 1.1
1423
1424 Options
1425 ~~~~~~~
1426
1427 - ``(Boolean) keepFocus``:
1428
1429
1430 ``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::
1431
1432 casper.then(function() {
1433 this.sendKeys('form.contact input#name', 'action', {keepFocus: true});
1434 this.click('form.contact ul.ui-autocomplete li.ui-menu-item:first- child a');
1435 });
1436
1437 - ``(String) modifiers``:
1438
1439 ``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::
1440
1441 casper.then(function() {
1442 this.sendKeys('document', 's', {modifiers: 'ctrl+alt+shift'});
1443 });
1444
1445 Available modifiers are:
1446
1447 - ``ctrl``
1448 - ``alt``
1449 - ``shift``
1450 - ``meta``
1451 - ``keypad``
1423 1452
1424 casper.then(function() {
1425 this.sendKeys('form.contact input#name', 'action', {keepFocus: true});
1426 this.click('form.contact ul.ui-autocomplete li.ui-menu-item:first-child a');
1427 });
1428 1453
1429 .. index:: auth 1454 .. index:: auth
1430 1455
......
...@@ -965,7 +965,7 @@ Casper.prototype.getElementAttr = function getElementAttr(selector, attribute) { ...@@ -965,7 +965,7 @@ Casper.prototype.getElementAttr = function getElementAttr(selector, attribute) {
965 /** 965 /**
966 * Retrieves the value of an attribute for each element matching the provided 966 * Retrieves the value of an attribute for each element matching the provided
967 * DOM CSS3/XPath selector. 967 * DOM CSS3/XPath selector.
968 * 968 *
969 * @param String selector A DOM CSS3/XPath selector 969 * @param String selector A DOM CSS3/XPath selector
970 * @param String attribute The attribute name to lookup 970 * @param String attribute The attribute name to lookup
971 * @return Array 971 * @return Array
...@@ -1531,6 +1531,11 @@ Casper.prototype.runStep = function runStep(step) { ...@@ -1531,6 +1531,11 @@ Casper.prototype.runStep = function runStep(step) {
1531 /** 1531 /**
1532 * Sends keys to given element. 1532 * Sends keys to given element.
1533 * 1533 *
1534 * Options:
1535 *
1536 * - eventType: "keypress", "keyup" or "keydown" (default: "keypress")
1537 * - modifiers: a string defining the key modifiers, eg. "alt", "alt+shift"
1538 *
1534 * @param String selector A DOM CSS3 compatible selector 1539 * @param String selector A DOM CSS3 compatible selector
1535 * @param String keys A string representing the sequence of char codes to send 1540 * @param String keys A string representing the sequence of char codes to send
1536 * @param Object options Options 1541 * @param Object options Options
...@@ -1543,18 +1548,20 @@ Casper.prototype.sendKeys = function(selector, keys, options) { ...@@ -1543,18 +1548,20 @@ Casper.prototype.sendKeys = function(selector, keys, options) {
1543 eventType: 'keypress' 1548 eventType: 'keypress'
1544 }, options || {}); 1549 }, options || {});
1545 var elemInfos = this.getElementInfo(selector), 1550 var elemInfos = this.getElementInfo(selector),
1546 tag = elemInfos.nodeName.toLowerCase(), 1551 tag = elemInfos.nodeName.toLowerCase(),
1547 type = utils.getPropertyPath(elemInfos, 'attributes.type'), 1552 type = utils.getPropertyPath(elemInfos, 'attributes.type'),
1548 supported = ["color", "date", "datetime", "datetime-local", "email", 1553 supported = ["color", "date", "datetime", "datetime-local", "email",
1549 "hidden", "month", "number", "password", "range", "search", 1554 "hidden", "month", "number", "password", "range", "search",
1550 "tel", "text", "time", "url", "week"]; 1555 "tel", "text", "time", "url", "week"],
1551 var isTextInput = false; 1556 isTextInput = false;
1552 if (tag === 'textarea' || (tag === 'input' && supported.indexOf(type) !== -1)) { 1557 if (tag === 'textarea' || (tag === 'input' && supported.indexOf(type) !== -1)) {
1553 // clicking on the input element brings it focus 1558 // clicking on the input element brings it focus
1554 isTextInput = true; 1559 isTextInput = true;
1555 this.click(selector); 1560 this.click(selector);
1556 } 1561 }
1557 this.page.sendEvent(options.eventType, keys); 1562 var modifiers = utils.computeModifier(options && options.modifiers,
1563 this.page.event.modifier)
1564 this.page.sendEvent(options.eventType, keys, null, null, modifiers);
1558 if (isTextInput && !options.keepFocus) { 1565 if (isTextInput && !options.keepFocus) {
1559 // remove the focus 1566 // remove the focus
1560 this.evaluate(function(selector) { 1567 this.evaluate(function(selector) {
......
...@@ -90,6 +90,30 @@ function clone(o) { ...@@ -90,6 +90,30 @@ function clone(o) {
90 exports.clone = clone; 90 exports.clone = clone;
91 91
92 /** 92 /**
93 * Computes a modifier string to its PhantomJS equivalent. A modifier string is
94 * in the form "ctrl+alt+shift".
95 *
96 * @param String modifierString Modifier string, eg. "ctrl+alt+shift"
97 * @param Object modifiers Modifiers definitions
98 * @return Number
99 */
100 function computeModifier(modifierString, modifiers) {
101 "use strict";
102 var modifier = 0,
103 checkKey = function(key) {
104 if (key in modifiers) return;
105 throw new CasperError(format('%s is not a supported key modifier', key));
106 };
107 if (!modifierString) return modifier;
108 var keys = modifierString.split('+');
109 keys.forEach(checkKey);
110 return keys.reduce(function(acc, key) {
111 return acc | modifiers[key];
112 }, modifier);
113 }
114 exports.computeModifier = computeModifier;
115
116 /**
93 * Dumps a JSON representation of passed value to the console. Used for 117 * Dumps a JSON representation of passed value to the console. Used for
94 * debugging purpose only. 118 * debugging purpose only.
95 * 119 *
......
...@@ -17,3 +17,25 @@ casper.test.begin('sendKeys() tests', 3, function(test) { ...@@ -17,3 +17,25 @@ casper.test.begin('sendKeys() tests', 3, function(test) {
17 test.done(); 17 test.done();
18 }); 18 });
19 }); 19 });
20
21 casper.test.begin('sendKeys() key modifiers tests', 1, function(test) {
22 casper.start().then(function() {
23 this.setContent([
24 '<input>',
25 '<script>var keys = []; window.addEventListener("keypress", function(e) {',
26 ' keys.push({code: e.which, alt: e.altKey, ctrl: e.ctrlKey});',
27 '})</script>'
28 ].join(''));
29 this.sendKeys('input', 'k');
30 this.sendKeys('input', 'k', {modifiers: "ctrl"});
31 this.sendKeys('input', 'k', {modifiers: "ctrl+alt"});
32 test.assertEquals(this.getGlobal('keys'),
33 [
34 {code: 107, alt: false, ctrl: false},
35 {code: 107, alt: false, ctrl: true},
36 {code: 107, alt: true, ctrl: true}
37 ], 'sendKeys() uses key modifiers');
38 }).run(function() {
39 test.done();
40 });
41 });
......
...@@ -52,6 +52,27 @@ casper.test.begin('utils.clone() tests', 2, function(test) { ...@@ -52,6 +52,27 @@ casper.test.begin('utils.clone() tests', 2, function(test) {
52 test.done(); 52 test.done();
53 }); 53 });
54 54
55 casper.test.begin('utils.computeModifier() tests', 7, function(test) {
56 var modifiers = require('webpage').create().event.modifier;
57 test.assertType(modifiers, "object");
58 test.assertEquals(utils.computeModifier("", modifiers), 0,
59 'computeModifier() computes a "none" modifier');
60 test.assertEquals(utils.computeModifier("alt", modifiers),
61 modifiers.alt,
62 'computeModifier() computes an "alt" modifier');
63 test.assertEquals(utils.computeModifier("ctrl+alt", modifiers),
64 modifiers.ctrl | modifiers.alt,
65 'computeModifier() computes a "ctrl+alt" modifier');
66 test.assertEquals(utils.computeModifier("ctrl+alt+shift", modifiers),
67 modifiers.ctrl | modifiers.alt | modifiers.shift,
68 'computeModifier() computes a "ctrl+alt+shift" modifier');
69 test.assertThrows(utils.computeModifier, ["chucknorris", modifiers],
70 'computeModifier() checks for a valid modifier');
71 test.assertThrows(utils.computeModifier, ["chuck+norris", modifiers],
72 'computeModifier() checks for a valid complex modifier');
73 test.done();
74 });
75
55 casper.test.begin('equals() tests', 23, function(test) { 76 casper.test.begin('equals() tests', 23, function(test) {
56 test.assert(utils.equals(null, null), 'equals() null equality'); 77 test.assert(utils.equals(null, null), 'equals() null equality');
57 test.assertNot(utils.equals(null, undefined), 'equals() null vs. undefined inequality'); 78 test.assertNot(utils.equals(null, undefined), 'equals() null vs. undefined inequality');
......