Commit f4514d88 f4514d88a47d25f4b7654c7d1db3bf11e323c6b8 by Nicolas Perriault

added Casper.sendKeys() - refs #302

to send native keyboard events to the element matching a given selector.
1 parent de27282e
......@@ -65,6 +65,7 @@ Also, `Casper.mouseEvent()` will now directly trigger an error on failure instea
- fixed [#290](https://github.com/n1k0/casperjs/issues/#290) - add a simplistic RPM spec file to make it easier to (un)install casperjs
- fixed [`utils.betterTypeOf()`](http://casperjs.org/api.html#casper.betterTypeOf) to properly handle `undefined` and `null` values
- fixed `Casper.die()` and `Casper.evaluateOrDie()` were not printing the error onto the console
- added [`Casper.sendKeys()`](http://casperjs.org/api.html#casper.sendKeys) to send native keyboard events to the element matching a given selector
- added [`Casper.getFormValues()`](http://casperjs.org/api.html#casper.getFormValues) to check for the field values of a given form
- added [`Tester.assertTextDoesntExist()`](http://casperjs.org/api.html#tester.assertTextDoesntExist)
- added `Tester.assertFalse()` as an alias of `Tester.assertNot()`
......
......@@ -221,7 +221,7 @@ Casper.prototype.back = function back() {
Casper.prototype.base64encode = function base64encode(url, method, data) {
"use strict";
return this.evaluate(function _evaluate(url, method, data) {
return window.__utils__.getBase64(url, method, data);
return __utils__.getBase64(url, method, data);
}, url, method, data);
};
......@@ -386,7 +386,11 @@ Casper.prototype.clear = function clear() {
Casper.prototype.click = function click(selector) {
"use strict";
this.checkStarted();
return this.mouseEvent('click', selector);
var success = this.mouseEvent('click', selector);
this.evaluate(function(selector) {
document.querySelector(selector).focus();
}, selector);
return success;
};
/**
......@@ -642,7 +646,7 @@ Casper.prototype.exists = function exists(selector) {
"use strict";
this.checkStarted();
return this.evaluate(function _evaluate(selector) {
return window.__utils__.exists(selector);
return __utils__.exists(selector);
}, selector);
};
......@@ -669,7 +673,7 @@ Casper.prototype.fetchText = function fetchText(selector) {
"use strict";
this.checkStarted();
return this.evaluate(function _evaluate(selector) {
return window.__utils__.fetchText(selector);
return __utils__.fetchText(selector);
}, selector);
};
......@@ -689,7 +693,7 @@ Casper.prototype.fill = function fill(selector, vals, submit) {
}
this.emit('fill', selector, vals, submit);
var fillResults = this.evaluate(function _evaluate(selector, values) {
return window.__utils__.fill(selector, values);
return __utils__.fill(selector, values);
}, selector, vals);
if (!fillResults) {
throw new CasperError("Unable to fill form");
......@@ -714,10 +718,10 @@ Casper.prototype.fill = function fill(selector, vals, submit) {
// Form submission?
if (submit) {
this.evaluate(function _evaluate(selector) {
var form = window.__utils__.findOne(selector);
var form = __utils__.findOne(selector);
var method = (form.getAttribute('method') || "GET").toUpperCase();
var action = form.getAttribute('action') || "unknown";
window.__utils__.log('submitting form to ' + action + ', HTTP ' + method, 'info');
__utils__.log('submitting form to ' + action + ', HTTP ' + method, 'info');
if (typeof form.submit === "function") {
form.submit();
} else {
......@@ -826,7 +830,7 @@ Casper.prototype.getElementBounds = function getElementBounds(selector) {
throw new CasperError("No element matching selector found: " + selector);
}
var clipRect = this.evaluate(function _evaluate(selector) {
return window.__utils__.getElementBounds(selector);
return __utils__.getElementBounds(selector);
}, selector);
if (!utils.isClipRect(clipRect)) {
throw new CasperError('Could not fetch boundaries for element matching selector: ' + selector);
......@@ -835,6 +839,23 @@ Casper.prototype.getElementBounds = function getElementBounds(selector) {
};
/**
* Retrieves information about the node matching the provided selector.
*
* @param String|Objects selector CSS3/XPath selector
* @return Object
*/
Casper.prototype.getElementInfo = function getElementInfo(selector) {
"use strict";
this.checkStarted();
if (!this.exists(selector)) {
throw new CasperError(f("Cannot get informations from %s: element not found.", selector));
}
return this.evaluate(function(selector) {
return __utils__.getElementInfo(selector);
}, selector);
};
/**
* Retrieves boundaries for all the DOM elements matching the provided DOM CSS3/XPath selector.
*
* @param String selector A DOM CSS3/XPath selector
......@@ -847,7 +868,7 @@ Casper.prototype.getElementsBounds = function getElementBounds(selector) {
throw new CasperError("No element matching selector found: " + selector);
}
return this.evaluate(function _evaluate(selector) {
return window.__utils__.getElementsBounds(selector);
return __utils__.getElementsBounds(selector);
}, selector);
};
......@@ -883,7 +904,7 @@ Casper.prototype.getGlobal = function getGlobal(name) {
result.value = JSON.stringify(window[name]);
} catch (e) {
var message = f("Unable to JSON encode window.%s: %s", name, e);
window.__utils__.log(message, "error");
__utils__.log(message, "error");
result.error = message;
}
return result;
......@@ -1015,7 +1036,7 @@ Casper.prototype.injectClientUtils = function injectClientUtils() {
"use strict";
this.checkStarted();
var clientUtilsInjected = this.page.evaluate(function() {
return typeof window.__utils__ === "object";
return typeof __utils__ === "object";
});
if (true === clientUtilsInjected) {
return;
......@@ -1120,7 +1141,7 @@ Casper.prototype.mouseEvent = function mouseEvent(type, selector) {
// PhantomJS doesn't provide native events for mouseover & mouseout
if (type === "mouseover" || type === "mouseout") {
return this.evaluate(function(type, selector) {
return window.__utils__.mouseEvent(type, selector);
return __utils__.mouseEvent(type, selector);
}, type, selector);
}
this.mouse.processEvent(type, selector);
......@@ -1307,6 +1328,45 @@ Casper.prototype.runStep = function runStep(step) {
};
/**
* Sends keys to given element.
*
* @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
* @return Casper
*/
Casper.prototype.sendKeys = function(selector, keys, options) {
"use strict";
if (phantom.version.major === 1 && phantom.version.minor < 7) {
throw new CasperError('sendKeys() requires PhantomJS >= 1.7');
}
this.checkStarted();
options = utils.mergeObjects({
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;
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);
if (isTextInput) {
// remove the focus
this.evaluate(function(selector) {
__utils__.findOne(selector).blur();
}, selector);
}
return this;
};
/**
* Sets current WebPage instance the credentials for HTTP authentication.
*
* @param String username
......@@ -1558,7 +1618,7 @@ Casper.prototype.visible = function visible(selector) {
"use strict";
this.checkStarted();
return this.evaluate(function _evaluate(selector) {
return window.__utils__.visible(selector);
return __utils__.visible(selector);
}, selector);
};
......
......@@ -41,7 +41,7 @@
* Casper client-side helpers.
*/
exports.ClientUtils = function ClientUtils(options) {
/*jshint maxstatements:30*/
/*jshint maxstatements:40*/
// private members
var BASE64_ENCODE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var BASE64_DECODE_CHARS = new Array(
......@@ -393,6 +393,33 @@
};
/**
* Retrieves information about the node matching the provided selector.
*
* @param String|Object selector CSS3/XPath selector
* @return Object
*/
this.getElementInfo = function getElementInfo(selector) {
var element = this.findOne(selector);
var bounds = this.getElementBounds(selector);
var attributes = {};
[].forEach.call(element.attributes, function(attr) {
attributes[attr.name.toLowerCase()] = attr.value;
});
return {
nodeName: element.nodeName.toLowerCase(),
attributes: attributes,
tag: element.outerHTML,
html: element.innerHTML,
text: element.innerText,
x: bounds.left,
y: bounds.top,
width: bounds.width,
height: bounds.height,
visible: this.visible(selector)
};
};
/**
* Retrieves a single DOM element matching a given XPath expression.
*
* @param String expression The XPath expression
......
/*jshint strict:false*/
/*global CasperError casper console phantom require*/
if (phantom.version.major === 1 && phantom.version.minor < 7) {
casper.test.pass('Skipping tests for PhantomJS < 1.7');
casper.test.done(1);
}
casper.start('tests/site/form.html', function() {
this.sendKeys('input[name="email"]', 'duke@nuk.em');
this.sendKeys('textarea', "Damn, I’m looking good.");
var values = this.getFormValues('form');
this.test.assertEquals(values['email'], 'duke@nuk.em',
'Casper.sendKeys() sends keys to given input');
this.test.assertEquals(values['content'], "Damn, I’m looking good.",
'Casper.sendKeys() sends keys to given textarea');
});
casper.run(function() {
this.test.done(2);
});
......@@ -97,6 +97,7 @@ function fakeDocument(html) {
// getElementsBounds
casper.start();
casper.then(function() {
this.test.comment('Casper.getElementsBounds()');
var html = '<div id="boxes">';
html += ' <div style="position:fixed;top:10px;left:11px;width:50px;height:60px"></div>';
html += ' <div style="position:fixed;top:20px;left:21px;width:70px;height:80px"></div>';
......@@ -110,6 +111,28 @@ function fakeDocument(html) {
});
})(casper);
(function(casper) {
// element information
casper.test.comment('ClientUtils.getElementInfo()');
casper.page.content = '<a href="plop" class="plip plup"><i>paf</i></a>';
var info = casper.getElementInfo('a.plip');
casper.test.assertEquals(info.nodeName, 'a', 'ClientUtils.getElementInfo() retrieves element name');
casper.test.assertEquals(info.attributes, {
'href': 'plop',
'class': 'plip plup'
}, 'ClientUtils.getElementInfo() retrieves element attributes');
casper.test.assertEquals(info.html, '<i>paf</i>', 'ClientUtils.getElementInfo() retrieves element html content');
casper.test.assertEquals(info.text, 'paf', 'ClientUtils.getElementInfo() retrieves element text');
casper.test.assert(info.x > 0, 'ClientUtils.getElementInfo() retrieves element x pos');
casper.test.assert(info.y > 0, 'ClientUtils.getElementInfo() retrieves element y pos');
casper.test.assert(info.width > 0, 'ClientUtils.getElementInfo() retrieves element width');
casper.test.assert(info.height > 0, 'ClientUtils.getElementInfo() retrieves element height');
casper.test.assert(info.visible, 'ClientUtils.getElementInfo() retrieves element visibility');
casper.test.assertEquals(info.tag, '<a href="plop" class="plip plup"><i>paf</i></a>',
'ClientUtils.getElementInfo() retrieves element whole tag contents');
})(casper);
casper.run(function() {
this.test.done(30);
this.test.done(40);
});
......