Commit 01c97982 01c97982e27853a89d55c5872aa5c6b1d6ff7f05 by Brikou CARRE

sync with latest master

2 parents f44d37b6 a05a2586
CasperJS Changelog
==================
2012-06-04, v0.6.10
-------------------
- fixed [#73](https://github.com/n1k0/casperjs/issues/73) - `Casper.download()` not working correctly with binaries
- fixed [#129](https://github.com/n1k0/casperjs/issues/129) - Can't put `//` comments in evaluate() function
- closed [#130](https://github.com/n1k0/casperjs/issues/130) - Added a `Dummy` [colorizer](http://casperjs.org/api.html#colorizer) class, in order to disable colors in console output
- fixed [#133](https://github.com/n1k0/casperjs/issues/133) - updated and fixed documentation about [extensibility](http://casperjs.org/extending.html)
- added `Casper.clickLabel()` for clicking on an element found by its `innerText` content
As a side note, the official website monolithic page has been split across several ones: http://casperjs.org/
2012-05-29, v0.6.9
------------------
- **BC BREAK:** PhantomJS 1.5 is now the minimal PhantomJS version supported.
- fixed [#114](https://github.com/n1k0/casperjs/issues/114) - ensured client-side utils are injected before any `evaluate()` call
- merged [#89](https://github.com/n1k0/casperjs/pull/89) - Support for more mouse events (@nrabinowitz)
- [added a new `error` event, better error reporting](https://github.com/n1k0/casperjs/commit/2e6988ae821b3251e063d11ba28af59b0683852a)
- fixed [#117](https://github.com/n1k0/casperjs/issues/117) - `fill()` coulnd't `submit()` a form with a submit input named *submit*
- merged [#122](https://github.com/n1k0/casperjs/pull/122) - allow downloads to be triggered by more than just `GET` requests
- closed [#57](https://github.com/n1k0/casperjs/issues/57) - added context to emitted test events + complete assertion framework refactor
- fixed loaded resources array is now reset adequately [reference discussion](https://groups.google.com/forum/?hl=fr?fromgroups#!topic/casperjs/TCkNzrj1IoA)
- fixed incomplete error message logged when passed an erroneous selector (xpath and css)
2012-05-20, v0.6.8
------------------
......
......@@ -4,7 +4,7 @@ CasperJS is a navigation scripting & testing utility for [PhantomJS](http://www.
It eases the process of defining a full navigation scenario and provides useful
high-level functions, methods & syntaxic sugar for doing common tasks such as:
- defining & ordering [navigation steps](http://casperjs.org/#phantom_Casper_run)
- defining & ordering [navigation steps](http://casperjs.org/#quickstart)
- [filling forms](http://casperjs.org/#phantom_Casper_fill)
- [clicking links](http://casperjs.org/#phantom_Casper_click)
- [capturing screenshots](http://casperjs.org/#phantom_Casper_captureSelector) of a page (or an area)
......@@ -12,15 +12,16 @@ high-level functions, methods & syntaxic sugar for doing common tasks such as:
- [logging](http://casperjs.org/#logging) & [events](http://casperjs.org/#events-filters)
- [downloading base64](http://casperjs.org/#phantom_Casper_download) encoded resources, even binary ones
- catching errors and react accordingly
- writing [functional test suite](http://casperjs.org/#testing), exporting results as JUnit XML (xUnit)
- writing [functional test suites](http://casperjs.org/#testing), exporting results as JUnit XML (xUnit)
Feel free to browse our [sample examples repository](https://github.com/n1k0/casperjs/tree/master/samples).
Browse the [sample examples repository](https://github.com/n1k0/casperjs/tree/master/samples).
Don't hesitate to pull request for any cool example of yours as well!
**Read the [full documentation](http://casperjs.org/) on casperjs dedicated website.**
Subscribe to the [project mailing-list](https://groups.google.com/forum/#!forum/casperjs)
Follow [@casperjs_org on twitter](https://twitter.com/casperjs_org).
## Contributing to the docs
......
......@@ -27,6 +27,10 @@
* DEALINGS IN THE SOFTWARE.
*
*/
if (phantom.version.major !== 1 || phantom.version.minor < 5) {
console.error('CasperJS needs at least PhantomJS v1.5.0');
phantom.exit(1);
}
/**
* Loads and initialize the CasperJS environment.
......@@ -151,7 +155,7 @@ phantom.loadCasper = function loadCasper() {
* TODO: remove when PhantomJS has full module support
*/
require = (function _require(require, requireDir) {
var phantomBuiltins = ['fs', 'webpage', 'webserver'];
var phantomBuiltins = ['fs', 'webpage', 'webserver', 'system'];
var phantomRequire = phantom.__orig__require = require;
var requireCache = {};
return function _require(path) {
......@@ -229,32 +233,6 @@ phantom.loadCasper = function loadCasper() {
};
/**
* Custom global error handler.
*/
phantom.onError = function phantom_onError(msg, backtrace) {
var c = require('colorizer').create();
var match = /^(.*): __mod_error(.*):: (.*)/.exec(msg);
var notices = [];
if (match && match.length === 4) {
notices.push(' in module ' + match[2]);
notices.push(' NOTICE: errors within modules cannot be backtraced yet.');
msg = match[3];
}
console.error(c.colorize(msg, 'RED_BAR', 80));
notices.forEach(function(notice) {
console.error(c.colorize(notice, 'COMMENT'));
});
backtrace.forEach(function(item) {
var message = require('fs').absolute(item.file) + ":" + c.colorize(item.line, "COMMENT");
if (item['function']) {
message += " in " + c.colorize(item['function'], "PARAMETER");
}
console.error(" " + message);
});
phantom.exit(1);
};
/**
* Initializes the CasperJS Command Line Interface.
*/
phantom.initCasperCli = function initCasperCli() {
......
#!/usr/bin/env python
import os
import subprocess
import sys
......
Subproject commit 100ffeb02edee0d765efd29ef897c25343e24b2d
Subproject commit 9ec9627555680a13c411d6b19541f3d23c8f30f7
......
......@@ -46,7 +46,7 @@ exports.selectXPath = function selectXPath(expression) {
type: 'xpath',
path: expression,
toString: function() {
return this.type + ' selector: ' + this.selector;
return this.type + ' selector: ' + this.path;
}
};
};
......@@ -66,7 +66,8 @@ var Casper = function Casper(options) {
// default options
this.defaults = {
clientScripts: [],
faultTolerant: true,
colorizerType: 'Colorizer',
exitOnError: true,
logLevel: "error",
httpStatusHandlers: {},
onAlert: null,
......@@ -88,10 +89,12 @@ var Casper = function Casper(options) {
timeout: null,
verbose: false
};
// options
this.options = utils.mergeObjects(this.defaults, options);
// properties
this.checker = null;
this.cli = phantom.casperArgs;
this.colorizer = colorizer.create();
this.colorizer = this.getColorizer();
this.currentUrl = 'about:blank';
this.currentHTTPStatus = 200;
this.defaultWaitTimeout = 5000;
......@@ -106,7 +109,6 @@ var Casper = function Casper(options) {
error: 'ERROR'
};
this.mouse = mouse.create(this);
this.options = utils.mergeObjects(this.defaults, options);
this.page = null;
this.pendingWait = false;
this.requestUrl = 'about:blank';
......@@ -121,10 +123,38 @@ var Casper = function Casper(options) {
this.steps = [];
this.test = tester.create(this);
// basic event handlers
// init phantomjs error handler
this.initErrorHandler();
this.on('error', function(msg, backtrace) {
var c = this.getColorizer();
var match = /^(.*): __mod_error(.*):: (.*)/.exec(msg);
var notices = [];
if (match && match.length === 4) {
notices.push(' in module ' + match[2]);
notices.push(' NOTICE: errors within modules cannot be backtraced yet.');
msg = match[3];
}
console.error(c.colorize(msg, 'RED_BAR', 80));
notices.forEach(function(notice) {
console.error(c.colorize(notice, 'COMMENT'));
});
backtrace.forEach(function(item) {
var message = fs.absolute(item.file) + ":" + c.colorize(item.line, "COMMENT");
if (item['function']) {
message += " in " + c.colorize(item['function'], "PARAMETER");
}
console.error(" " + message);
});
});
// deprecated feature event handler
this.on('deprecated', function onDeprecated(message) {
this.echo('[deprecated] ' + message, 'COMMENT');
});
// dispatching an event when instance has been constructed
this.emit('init');
};
// Casper class is an EventEmitter
......@@ -156,7 +186,6 @@ Casper.prototype.back = function back() {
* @return string Base64 encoded result
*/
Casper.prototype.base64encode = function base64encode(url, method, data) {
this.injectClientUtils();
return this.evaluate(function _evaluate(url, method, data) {
return __utils__.getBase64(url, method, data);
}, { url: url, method: method, data: data });
......@@ -227,11 +256,7 @@ Casper.prototype.checkStep = function checkStep(self, onComplete) {
clearInterval(self.checker);
self.emit('run.complete');
if (utils.isFunction(onComplete)) {
try {
onComplete.call(self, self);
} catch (err) {
self.log("Could not complete final step: " + err, "error");
}
} else {
// default behavior is to exit
self.exit();
......@@ -263,26 +288,19 @@ Casper.prototype.clear = function clear() {
* @return Boolean
*/
Casper.prototype.click = function click(selector) {
this.log("Click on selector: " + selector, "debug");
if (arguments.length > 1) {
this.emit("deprecated", "The click() method does not process the fallbackToHref argument since 0.6");
}
if (!this.exists(selector)) {
throw new CasperError("Cannot click on unexistent selector: " + selector);
}
var clicked = this.evaluate(function _evaluate(selector) {
return __utils__.click(selector);
}, { selector: selector });
if (!clicked) {
// fallback onto native QtWebKit mouse events
try {
this.mouse.click(selector);
} catch (e) {
this.log(f("Error while trying to click on selector %s: %s", selector, e), "error");
return false;
}
}
return true;
return this.mouseEvent('click', selector);
};
/**
* Emulates a click on the element having `label` as innerText. The first
* element matching this label will be selected, so use with caution.
*
* @param String label Element innerText value
* @return Boolean
*/
Casper.prototype.clickLabel = function clickLabel(label) {
var selector = exports.selectXPath('//*[text()="' + label.toString() + '"]');
return this.click(selector);
};
/**
......@@ -351,10 +369,10 @@ Casper.prototype.die = function die(message, status) {
* @param String targetPath The destination file path
* @return Casper
*/
Casper.prototype.download = function download(url, targetPath) {
Casper.prototype.download = function download(url, targetPath, method, data) {
var cu = require('clientutils').create();
try {
fs.write(targetPath, cu.decode(this.base64encode(url)), 'w');
fs.write(targetPath, cu.decode(this.base64encode(url, method, data)), 'wb');
this.emit('downloaded.file', targetPath);
this.log(f("Downloaded and saved resource in %s", targetPath));
} catch (e) {
......@@ -422,6 +440,9 @@ Casper.prototype.echo = function echo(text, style, pad) {
* @see WebPage#evaluate
*/
Casper.prototype.evaluate = function evaluate(fn, context) {
// ensure client utils are always injected
this.injectClientUtils();
// function processing
context = utils.isObject(context) ? context : {};
var newFn = require('injector').create(fn).process(context);
return this.page.evaluate(newFn);
......@@ -533,7 +554,12 @@ Casper.prototype.fill = function fill(selector, vals, submit) {
var method = (form.getAttribute('method') || "GET").toUpperCase();
var action = form.getAttribute('action') || "unknown";
__utils__.log('submitting form to ' + action + ', HTTP ' + method, 'info');
if (typeof form.submit === "function") {
form.submit();
} else {
// http://www.spiration.co.uk/post/1232/Submit-is-not-a-function
form.submit.click();
}
}, { selector: selector });
}
};
......@@ -553,6 +579,16 @@ Casper.prototype.forward = function forward(then) {
};
/**
* Creates a new Colorizer instance. Sets `Casper.options.type` to change the
* colorizer type name (see the `colorizer` module).
*
* @return Object
*/
Casper.prototype.getColorizer = function getColorizer() {
return colorizer.create(this.options.colorizerType || 'Colorizer');
};
/**
* Retrieves current document url.
*
* @return String
......@@ -621,11 +657,25 @@ Casper.prototype.getTitle = function getTitle() {
};
/**
* Initializes PhantomJS error handler.
*
*/
Casper.prototype.initErrorHandler = function initErrorHandler() {
var casper = this;
phantom.onError = function phantom_onError(msg, backtrace) {
casper.emit('error', msg, backtrace);
if (casper.options.exitOnError === true) {
casper.exit(1);
}
};
};
/**
* Injects Client-side utilities in current page context.
*
*/
Casper.prototype.injectClientUtils = function injectClientUtils() {
var clientUtilsInjected = this.evaluate(function() {
var clientUtilsInjected = this.page.evaluate(function() {
return typeof __utils__ === "object";
});
if (true === clientUtilsInjected) {
......@@ -677,17 +727,36 @@ Casper.prototype.log = function log(message, level, space) {
};
/**
* Emulates a click on an HTML element matching a given CSS3 selector,
* using the mouse pointer.
* Emulates an event on the element from the provided selector using the mouse
* pointer, if possible.
*
* In case of success, `true` is returned, `false` otherwise.
*
* @param String type Type of event to emulate
* @param String selector A DOM CSS3 compatible selector
* @return Casper
* @deprecated
* @since 0.6
* @return Boolean
*/
Casper.prototype.mouseClick = function mouseClick(selector) {
this.emit("deprecated", "The mouseClick() method has been deprecated since 0.6; use click() instead");
return this.click(selector);
Casper.prototype.mouseEvent = function mouseEvent(type, selector) {
this.log("Mouse event '" + type + "' on selector: " + selector, "debug");
if (!this.exists(selector)) {
throw new CasperError("Cannot dispatch an event on nonexistent selector: " + selector);
}
var eventSuccess = this.evaluate(function(type, selector) {
return __utils__.mouseEvent(type, selector);
}, {
type: type,
selector: selector
});
if (!eventSuccess) {
// fallback onto native QtWebKit mouse events
try {
this.mouse.processEvent(type, selector);
} catch (e) {
this.log(f("Couldn't emulate event '%s' on %s: %s", type, selector, e), "error");
return false;
}
}
return true;
};
/**
......@@ -737,10 +806,12 @@ Casper.prototype.open = function open(location, settings) {
}
}
this.emit('open', this.requestUrl, settings);
this.log(f('opening url: %s, HTTP %s', this.requestUrl, settings.method.toUpperCase()), "debug");
this.page.openUrl(this.requestUrl, {
operation: settings.method,
data: settings.data
}, this.page.settings);
this.resources = [];
return this;
};
......@@ -770,17 +841,18 @@ Casper.prototype.resourceExists = function resourceExists(test) {
var testFn;
switch (utils.betterTypeOf(test)) {
case "string":
testFn = function _test(res) {
testFn = function _testResourceExists_String(res) {
return res.url.search(test) !== -1;
};
break;
case "regexp":
testFn = function _test(res) {
testFn = function _testResourceExists_Regexp(res) {
return test.test(res.url);
};
break;
case "function":
testFn = test;
testFn.name = "_testResourceExists_Function";
break;
default:
throw new CasperError("Invalid type");
......@@ -834,16 +906,7 @@ Casper.prototype.runStep = function runStep(step) {
}, this.options.stepTimeout, this, new Date().getTime(), this.step);
}
this.emit('step.start', step);
try {
stepResult = step.call(this, this);
} catch (e) {
this.emit('step.error', e);
if (this.options.faultTolerant) {
this.log("Step error: " + e, "error");
} else {
throw e;
}
}
if (utils.isFunction(this.options.onStepComplete)) {
this.options.onStepComplete.call(this, this, stepResult);
}
......@@ -922,8 +985,8 @@ Casper.prototype.start = function start(location, then) {
}, this.options.timeout, this);
}
if (utils.isString(location) && location.length > 0) {
return this.thenOpen(location, utils.isFunction(then) ? then : this.createStep(function _step(self) {
self.log("start page is loaded", "debug");
return this.thenOpen(location, utils.isFunction(then) ? then : this.createStep(function _step() {
this.log("start page is loaded", "debug");
}));
}
return this;
......@@ -1086,7 +1149,7 @@ Casper.prototype.wait = function wait(timeout, then) {
return this.then(function _step() {
this.waitStart();
setTimeout(function _check(self) {
self.log(f("wait() finished wating for %dms.", timeout), "info");
self.log(f("wait() finished waiting for %dms.", timeout), "info");
if (then) {
then.call(self, self);
}
......@@ -1294,7 +1357,6 @@ function createPage(casper) {
};
page.onLoadStarted = function onLoadStarted() {
casper.loadInProgress = true;
casper.resources = [];
casper.emit('load.started');
};
page.onLoadFinished = function onLoadFinished(status) {
......
......@@ -29,13 +29,13 @@
*/
(function(exports) {
exports.create = function create() {
return new ClientUtils();
return new this.ClientUtils();
};
/**
* Casper client-side helpers.
*/
ClientUtils = function ClientUtils() {
exports.ClientUtils = function ClientUtils() {
var BASE64_ENCODE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var BASE64_DECODE_CHARS = new Array(
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
......@@ -56,16 +56,7 @@
* @return Boolean
*/
this.click = function click(selector) {
var elem = this.findOne(selector);
if (!elem) {
this.log("click(): Couldn't find any element matching '" + selector + "' selector", "error");
return false;
}
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window, 1, 1, 1, 1, 1, false, false, false, false, 0, elem);
// dispatchEvent return value is false if at least one of the event
// handlers which handled this event called preventDefault
return elem.dispatchEvent(evt);
return this.mouseEvent('click', selector);
};
/**
......@@ -380,19 +371,6 @@
};
/**
* Removes all DOM elements matching a given XPath expression.
*
* @param String expression The XPath expression
* @return Array
*/
this.removeElementsByXPath = function removeElementsByXPath(expression) {
var a = document.evaluate(expression, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0; i < a.snapshotLength; i++) {
a.snapshotItem(i).parentNode.removeChild(a.snapshotItem(i));
}
};
/**
* Logs a message. Will format the message a way CasperJS will be able
* to log phantomjs side.
*
......@@ -404,6 +382,26 @@
};
/**
* Dispatches a mouse event to the DOM element behind the provided selector.
*
* @param String type Type of event to dispatch
* @param String selector A CSS3 selector to the element to click
* @return Boolean
*/
this.mouseEvent = function mouseEvent(type, selector) {
var elem = this.findOne(selector);
if (!elem) {
this.log("mouseEvent(): Couldn't find any element matching '" + selector + "' selector", "error");
return false;
}
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent(type, true, true, window, 1, 1, 1, 1, 1, false, false, false, false, 0, elem);
// dispatchEvent return value is false if at least one of the event
// handlers which handled this event called preventDefault
return elem.dispatchEvent(evt);
};
/**
* Processes a selector input, either as a string or an object.
*
* If passed an object, if must be of the form:
......@@ -420,7 +418,7 @@
this.processSelector = function processSelector(selector) {
var selectorObject = {
toString: function toString() {
return this.type + ' selector: ' + this.selector;
return this.type + ' selector: ' + this.path;
}
};
if (typeof selector === "string") {
......@@ -444,6 +442,19 @@
};
/**
* Removes all DOM elements matching a given XPath expression.
*
* @param String expression The XPath expression
* @return Array
*/
this.removeElementsByXPath = function removeElementsByXPath(expression) {
var a = document.evaluate(expression, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0; i < a.snapshotLength; i++) {
a.snapshotItem(i).parentNode.removeChild(a.snapshotItem(i));
}
};
/**
* Sets a field (or a set of fields) value. Fails silently, but log
* error messages.
*
......@@ -546,14 +557,19 @@
*/
this.visible = function visible(selector) {
try {
var el = this.findOne(selector);
return el && el.style.visibility !== 'hidden' && el.offsetHeight > 0 && el.offsetWidth > 0;
var comp,
el = this.findOne(selector);
if (el) {
comp = window.getComputedStyle(el, null);
return comp.visibility !== 'hidden' && comp.display !== 'none' && el.offsetHeight > 0 && el.offsetWidth > 0;
}
return false;
} catch (e) {
return false;
}
};
};
exports.ClientUtils = ClientUtils;
// silly "hack" to force having an instance available
exports.__utils__ = new exports.ClientUtils();
......
......@@ -31,13 +31,19 @@
var fs = require('fs');
var utils = require('utils');
exports.create = function create() {
return new Colorizer();
exports.create = function create(type) {
if (!type) {
return;
}
if (!(type in exports)) {
throw new Error(utils.format('Unsupported colorizer type "%s"', type));
}
return new exports[type]();
};
/**
* This is a port of lime colorizer.
* http://trac.symfony-project.org/browser/tools/lime/trunk/lib/lime.php)
* http://trac.symfony-project.org/browser/tools/lime/trunk/lib/lime.php
*
* (c) Fabien Potencier, Symfony project, MIT license
*/
......@@ -102,3 +108,17 @@ var Colorizer = function Colorizer() {
};
};
exports.Colorizer = Colorizer;
/**
* Dummy colorizer. Does basically nothing.
*
*/
var Dummy = function Dummy() {
this.colorize = function colorize(text, styleName, pad) {
return text;
};
this.format = function format(text, style, pad){
return text;
};
};
exports.Dummy = Dummy;
......
......@@ -67,7 +67,9 @@ var FunctionArgsInjector = function FunctionArgsInjector(fn) {
throw new CasperError("Unable to process function " + this.fn.toString());
}
var inject = this.getArgsInjectionString(fnObj.args, values);
return 'function ' + (fnObj.name || '') + '(){' + inject + fnObj.body + '}';
var newFn = new Function([inject, fnObj.body].join('\n'));
newFn.name = fnObj.name || '';
return newFn;
};
this.getArgsInjectionString = function getArgsInjectionString(args, values) {
......
......@@ -39,7 +39,11 @@ var Mouse = function Mouse(casper) {
throw new CasperError('Mouse() needs a Casper instance');
}
var supportedEvents = ['mouseup', 'mousedown', 'click', 'mousemove'];
var slice = Array.prototype.slice;
var nativeEvents = ['mouseup', 'mousedown', 'click', 'mousemove'];
var emulatedEvents = ['mouseover', 'mouseout'];
var supportedEvents = nativeEvents.concat(emulatedEvents);
function computeCenter(selector) {
var bounds = casper.getElementBounds(selector);
......@@ -54,7 +58,10 @@ var Mouse = function Mouse(casper) {
if (!utils.isString(type) || supportedEvents.indexOf(type) === -1) {
throw new CasperError('Mouse.processEvent(): Unsupported mouse event type: ' + type);
}
args = Array.prototype.slice.call(args); // cast Arguments -> Array
if (emulatedEvents.indexOf(type) > -1) {
casper.log("Mouse.processEvent(): no native fallback for type " + type, "warning");
}
args = slice.call(args); // cast Arguments -> Array
casper.emit('mouse.' + type.replace('mouse', ''), args);
switch (args.length) {
case 0:
......@@ -62,9 +69,6 @@ var Mouse = function Mouse(casper) {
case 1:
// selector
var selector = args[0];
if (!utils.isString(selector)) {
throw new CasperError('Mouse.processEvent(): No valid CSS selector passed: ' + selector);
}
casper.page.sendEvent.apply(casper.page, [type].concat(computeCenter(selector)));
break;
case 2:
......@@ -79,6 +83,10 @@ var Mouse = function Mouse(casper) {
}
}
this.processEvent = function() {
processEvent(arguments[0], slice.call(arguments, 1));
};
this.click = function click() {
processEvent('click', arguments);
};
......
......@@ -66,9 +66,18 @@ var Tester = function Tester(casper, options) {
};
// events
casper.on('error', function(msg, backtrace) {
var line = 0;
try {
line = backtrace[0].line;
} catch (e) {}
this.test.uncaughtError(msg, this.test.currentTestFile, line);
this.test.done();
});
casper.on('step.error', function onStepError(e) {
casper.test.fail(e);
casper.test.done();
this.test.uncaughtError(e, this.test.currentTestFile);
this.test.done();
});
this.on('success', function onSuccess(success) {
......@@ -76,39 +85,49 @@ var Tester = function Tester(casper, options) {
});
this.on('fail', function onFail(failure) {
// export
this.exporter.addFailure(fs.absolute(failure.file), failure.message, failure.details || "test failed", failure.type || "unknown");
this.testResults.failures.push(failure);
// special printing
if (failure.type) {
this.comment(' type: ' + failure.type);
}
if (failure.details) {
this.comment(' details: ' + failure.details);
}
if (failure.values && Object.keys(failure.values).length > 0) {
for (var name in failure.values) {
this.comment(' ' + name + ': ' + utils.serialize(failure.values[name]));
}
}
});
// methods
/**
* Asserts that a condition strictly resolves to true.
* Asserts that a condition strictly resolves to true. Also returns an
* "assertion object" containing useful informations about the test case
* results.
*
* This method is also used as the base one used for all other `assert*`
* family methods; supplementary informations are then passed using the
* `context` argument.
*
* @param Boolean subject
* @param Boolean subject The condition to test
* @param String message Test description
* @param Object|null context Assertion context object (Optional)
* @return Object An assertion result object
*/
this.assert = this.assertTrue = function assert(subject, message) {
var status = this.options.passText, eventName;
if (subject === true) {
eventName = 'success';
style = 'INFO';
this.testResults.passed++;
} else {
eventName = 'fail';
status = this.options.failText;
style = 'RED_BAR';
this.testResults.failed++;
}
this.emit(eventName, {
this.assert = this.assertTrue = function assert(subject, message, context) {
return this.processAssertionResult(utils.mergeObjects({
success: subject === true,
type: "assert",
details: "test failed",
details: "Subject's not a strict boolean true",
message: message,
file: this.currentTestFile,
values: {
subject: subject
}
});
casper.echo([this.colorize(status, style), this.formatMessage(message)].join(' '));
}, context || {}));
};
/**
......@@ -116,27 +135,13 @@ var Tester = function Tester(casper, options) {
*
* @param Mixed subject The value to test
* @param Mixed expected The expected value
* @param String message Test description
* @param String message Test description (Optional)
* @return Object An assertion result object
*/
this.assertEquals = this.assertEqual = function assertEquals(subject, expected, message) {
var eventName;
message = message || "";
if (this.testEquals(subject, expected)) {
eventName = "success";
casper.echo(this.colorize(this.options.passText, 'INFO') + ' ' + this.formatMessage(message));
this.testResults.passed++;
} else {
eventName = "fail";
casper.echo(this.colorize(this.options.failText, 'RED_BAR') + ' ' + this.formatMessage(message, 'WARNING'));
this.comment(' got: ' + utils.serialize(subject));
this.comment(' expected: ' + utils.serialize(expected));
this.testResults.failed++;
}
this.emit(eventName, {
return this.assert(this.testEquals(subject, expected), message, {
type: "assertEquals",
message: message,
details: f("test failed; expected: %s; got: %s", expected, subject),
file: this.currentTestFile,
details: "Subject didn't equal the expected value",
values: {
subject: subject,
expected: expected
......@@ -149,27 +154,13 @@ var Tester = function Tester(casper, options) {
*
* @param Mixed subject The value to test
* @param Mixed expected The unwanted value
* @param String message Test description
* @param String|null message Test description (Optional)
* @return Object An assertion result object
*/
this.assertNotEquals = function assertNotEquals(subject, shouldnt, message) {
var eventName;
message = message || "";
if (!this.testEquals(subject, shouldnt)) {
eventName = "success";
casper.echo(this.colorize(this.options.passText, 'INFO') + ' ' + this.formatMessage(message));
this.testResults.passed++;
} else {
eventName = "fail";
casper.echo(this.colorize(this.options.failText, 'RED_BAR') + ' ' + this.formatMessage(message, 'WARNING'));
this.comment(' got: ' + utils.serialize(subject));
this.comment(' shouldnt: ' + utils.serialize(shouldnt));
this.testResults.failed++;
}
this.emit(eventName, {
return this.assert(!this.testEquals(subject, shouldnt), message, {
type: "assertNotEquals",
message: message,
details: f("test failed; shouldnt: %s; got: %s", shouldnt, subject),
file: this.currentTestFile,
details: "Subject actually equals to what it shouldn't be",
values: {
subject: subject,
shouldnt: shouldnt
......@@ -182,10 +173,18 @@ var Tester = function Tester(casper, options) {
*
* @param Function fn A function to be evaluated in remote DOM
* @param String message Test description
* @param Object context Object containing the parameters to inject into the function (optional)
* @param Object params Object containing the parameters to inject into the function (optional)
* @return Object An assertion result object
*/
this.assertEval = function assertEval(fn, message, context) {
return this.assert(casper.evaluate(fn, context), message);
this.assertEval = this.assertEvaluate = function assertEval(fn, message, params) {
return this.assert(casper.evaluate(fn, params), message, {
type: "assertEval",
details: "Function didn't evaluate to true",
values: {
fn: fn,
params: params
}
});
};
/**
......@@ -194,11 +193,22 @@ var Tester = function Tester(casper, options) {
*
* @param Function fn The function to be evaluated in remote DOM
* @param Boolean expected The expected value
* @param String message Test description
* @param Object context Object containing the parameters to inject into the function (optional)
*/
this.assertEvalEquals = this.assertEvalEqual = function assertEvalEquals(fn, expected, message, context) {
return this.assertEquals(casper.evaluate(fn, context), expected, message);
* @param String|null message Test description
* @param Object|null params Object containing the parameters to inject into the function (optional)
* @return Object An assertion result object
*/
this.assertEvalEquals = this.assertEvalEqual = function assertEvalEquals(fn, expected, message, params) {
var subject = casper.evaluate(fn, params);
return this.assert(this.testEquals(subject, expected), message, {
type: "assertEvalEquals",
details: "Evaluated function didn't return the expected value",
values: {
fn: fn,
params: params,
subject: subject,
expected: expected
}
});
};
/**
......@@ -207,9 +217,16 @@ var Tester = function Tester(casper, options) {
*
* @param String selector Selector expression
* @param String message Test description
* @return Object An assertion result object
*/
this.assertExists = this.assertExist = function assertExists(selector, message) {
return this.assert(casper.exists(selector), message);
this.assertExists = this.assertExist = this.assertSelectorExists = this.assertSelectorExist = function assertExists(selector, message) {
return this.assert(casper.exists(selector), message, {
type: "assertExists",
details: f("No element matching selector %s was found", selector),
values: {
selector: selector
}
});
};
/**
......@@ -218,9 +235,16 @@ var Tester = function Tester(casper, options) {
*
* @param String selector Selector expression
* @param String message Test description
* @return Object An assertion result object
*/
this.assertDoesntExist = this.assertNotExists = function assertDoesntExist(selector, message) {
return this.assertNot(casper.exists(selector), message);
return this.assert(!casper.exists(selector), message, {
type: "assertDoesntExist",
details: f("At least one element matching selector %s was found", selector),
values: {
selector: selector
}
});
};
/**
......@@ -228,9 +252,18 @@ var Tester = function Tester(casper, options) {
*
* @param Number status HTTP status code
* @param String message Test description
* @return Object An assertion result object
*/
this.assertHttpStatus = function assertHttpStatus(status, message) {
return this.assertEquals(casper.currentHTTPStatus, status, message || f("HTTP status code is %d", status));
var currentHTTPStatus = casper.currentHTTPStatus;
return this.assert(this.testEquals(casper.currentHTTPStatus, status), message, {
type: "assertHttpStatus",
details: f("HTTP status code is not %s, but %s", status, currentHTTPStatus),
values: {
current: currentHTTPStatus,
expected: status
}
});
};
/**
......@@ -239,25 +272,12 @@ var Tester = function Tester(casper, options) {
* @param String subject The string to test
* @param RegExp pattern A RegExp object instance
* @param String message Test description
* @return Object An assertion result object
*/
this.assertMatch = this.assertMatches = function assertMatch(subject, pattern, message) {
var eventName;
if (pattern.test(subject)) {
eventName = "success";
casper.echo(this.colorize(this.options.passText, 'INFO') + ' ' + this.formatMessage(message));
this.testResults.passed++;
} else {
eventName = "fail";
casper.echo(this.colorize(this.options.failText, 'RED_BAR') + ' ' + this.formatMessage(message, 'WARNING'));
this.comment(' subject: ' + subject);
this.comment(' pattern: ' + pattern.toString());
this.testResults.failed++;
}
this.emit(eventName, {
return this.assert(pattern.test(subject), message, {
type: "assertMatch",
message: message,
details: f("test failed; subject: %s; pattern: %s", subject, pattern.toString()),
file: this.currentTestFile,
details: "Subject didn't match the provided pattern",
values: {
subject: subject,
pattern: pattern
......@@ -268,11 +288,18 @@ var Tester = function Tester(casper, options) {
/**
* Asserts a condition resolves to false.
*
* @param Boolean condition
* @param Boolean condition The condition to test
* @param String message Test description
* @return Object An assertion result object
*/
this.assertNot = function assertNot(condition, message) {
return this.assert(!condition, message);
return this.assert(!condition, message, {
type: "assertNot",
details: "The condition is not falsy",
values: {
condition: condition
}
});
};
/**
......@@ -282,13 +309,22 @@ var Tester = function Tester(casper, options) {
* @param Function fn The function to test
* @param Array args The arguments to pass to the function
* @param String message Test description
* @return Object An assertion result object
*/
this.assertRaises = this.assertRaise = function assertRaises(fn, args, message) {
this.assertRaises = this.assertRaise = this.assertThrows = function assertRaises(fn, args, message) {
var context = {
type: "assertRaises",
details: "Function didn't raise any error"
};
try {
fn.apply(null, args);
this.fail(message);
} catch (e) {
this.pass(message);
this.assert(false, message, context);
} catch (error) {
this.assert(true, message, utils.mergeObjects(context, {
values: {
error: error
}
}));
}
};
......@@ -297,20 +333,16 @@ var Tester = function Tester(casper, options) {
*
* @param Function/String test A test function that is called with every response
* @param String message Test description
* @return Object An assertion result object
*/
this.assertResourceExists = this.assertResourceExist = function assertResourceExists(test, message) {
return this.assert(casper.resourceExists(test), message);
};
/**
* Asserts that at least an element matching the provided selector expression
* exists in remote DOM.
*
* @param String selector A selector expression string
* @param String message Test description
*/
this.assertSelectorExists = this.assertSelectorExist = function assertSelectorExists(selector, message) {
return this.assert(casper.exists(selector), message);
return this.assert(casper.resourceExists(test), message, {
type: "assertResourceExists",
details: "Resource was not found",
values: {
test: test
}
});
};
/**
......@@ -318,11 +350,19 @@ var Tester = function Tester(casper, options) {
*
* @param String text Text to be found
* @param String message Test description
* @return Object An assertion result object
*/
this.assertTextExists = this.assertTextExist = function assertTextExists(text, message) {
return this.assert((casper.evaluate(function _evaluate() {
var textFound = (casper.evaluate(function _evaluate() {
return document.body.innerText;
}).indexOf(text) != -1), message);
}).indexOf(text) != -1);
return this.assert(textFound, message, {
type: "assertTextExists",
details: "Text was not found within the document body textual contents",
values: {
text: text
}
});
};
/**
......@@ -330,20 +370,39 @@ var Tester = function Tester(casper, options) {
*
* @param String expected The expected title string
* @param String message Test description
* @return Object An assertion result object
*/
this.assertTitle = function assertTitle(expected, message) {
return this.assertEquals(casper.getTitle(), expected, message);
var currentTitle = casper.getTitle();
return this.assert(this.testEquals(currentTitle, expected), message, {
type: "assertTitle",
details: "Page title is not the one expected",
values: {
subject: currentTitle,
expected: expected
}
});
};
/**
* Asserts that the provided input is of the given type.
* Asserts that the provided subject is of the given type.
*
* @param mixed input The value to test
* @param mixed subject The value to test
* @param String type The javascript type name
* @param String message Test description
* @return Object An assertion result object
*/
this.assertType = function assertType(input, type, message) {
return this.assertEquals(utils.betterTypeOf(input), type, message);
this.assertType = function assertType(subject, type, message) {
var actual = utils.betterTypeOf(subject);
return this.assert(this.testEquals(actual, type), message, {
type: "assertType",
details: f("Expected type %s, got %s", typeof subject, actual),
values: {
subject: subject,
type: type,
actual: actual
}
});
};
/**
......@@ -352,11 +411,24 @@ var Tester = function Tester(casper, options) {
*
* @param RegExp pattern A RegExp object instance
* @param String message Test description
* @return Object An assertion result object
*/
this.assertUrlMatch = this.assertUrlMatches = function assertUrlMatch(pattern, message) {
return this.assertMatch(casper.getCurrentUrl(), pattern, message);
var currentUrl = casper.getCurrentUrl();
return this.assert(pattern.test(currentUrl), message, {
type: "assertUrlMatch",
details: "Current url did not match the provided pattern",
values: {
currentUrl: currentUrl,
pattern: pattern
}
});
};
/**
* Prints out a colored bar onto the console.
*
*/
this.bar = function bar(text, style) {
casper.echo(text, style, this.options.pad);
};
......@@ -409,14 +481,7 @@ var Tester = function Tester(casper, options) {
throw e;
}
this.currentTestFile = file;
try {
new Function('casper', phantom.getScriptCode(file))(casper);
} catch (e) {
// do not abort the whole suite, just fail fast displaying the
// caught error and process next suite
this.fail(e);
this.done();
}
phantom.injectJs(file);
};
/**
......@@ -425,7 +490,10 @@ var Tester = function Tester(casper, options) {
* @param String message
*/
this.fail = function fail(message) {
this.assert(false, message);
return this.assert(false, message, {
type: "fail",
details: "explicit call to fail()"
});
};
/**
......@@ -482,7 +550,35 @@ var Tester = function Tester(casper, options) {
* @param String message
*/
this.pass = function pass(message) {
this.assert(true, message);
return this.assert(true, message, {
type: "pass",
details: "explicit call to pass()"
});
};
/**
* Processes an assertion result by emitting the appropriate event and
* printing result onto the console.
*
* @param Object result An assertion result object
* @return Object The passed assertion result Object
*/
this.processAssertionResult = function processAssertionResult(result) {
var eventName, style, status;
if (result.success === true) {
eventName = 'success';
style = 'INFO';
status = this.options.passText;
this.testResults.passed++;
} else {
eventName = 'fail';
style = 'RED_BAR';
status = this.options.failText;
this.testResults.failed++;
}
casper.echo([this.colorize(status, style), this.formatMessage(result.message)].join(' '));
this.emit(eventName, result);
return result;
};
/**
......@@ -496,16 +592,12 @@ var Tester = function Tester(casper, options) {
}
casper.echo(f("\nDetails for the %d failed test%s:\n", failures.length, failures.length > 1 ? "s" : ""), "PARAMETER");
failures.forEach(function _forEach(failure) {
var message, line;
if (utils.isType(failure.message, "object") && failure.message.stack) {
line = failure.message.line ? failure.message.line : 0;
message = failure.message.stack;
} else {
line = 0;
var type, message, line;
type = failure.type || "unknown";
line = ~~failure.line;
message = failure.message;
}
casper.echo(f('In %s:%d', failure.file, line));
casper.echo(f(' %s', message), "COMMENT");
casper.echo(f('In %s:%s', failure.file, line));
casper.echo(f(' %s: %s', type, message || "(no message was entered)"), "COMMENT");
});
};
......@@ -595,12 +687,7 @@ var Tester = function Tester(casper, options) {
this.runTest = function runTest(testFile) {
this.bar(f('Test file: %s', testFile), 'INFO_BAR');
this.running = true; // this.running is set back to false with done()
try {
this.exec(testFile);
} catch (e) {
this.fail(e);
this.done();
}
};
/**
......@@ -630,6 +717,27 @@ var Tester = function Tester(casper, options) {
}
return v1 === v2;
};
/**
* Processes an error caught while running tests contained in a given test
* file.
*
* @param Error|String error The error
* @param String file Test file where the error occured
* @param Number line Line number (optional)
*/
this.uncaughtError = function uncaughtError(error, file, line) {
return this.processAssertionResult({
success: false,
type: "uncaughtError",
file: file,
line: ~~line || "unknown",
message: utils.isObject(error) ? error.message : error,
values: {
error: error
}
});
};
};
// Tester class is an EventEmitter
......
......@@ -131,27 +131,22 @@ function format(f) {
exports.format = format;
/**
* Inherit the prototype methods from one constructor into another, also
* exposes the `__super__` property to child class.
* Inherit the prototype methods from one constructor into another.
*
* @param Function child Constructor function which needs to inherit the
* @param {function} ctor Constructor function which needs to inherit the
* prototype.
* @param Function parent Constructor function to inherit prototype from.
* @return Function The child class
* @param {function} superCtor Constructor function to inherit prototype from.
*/
function inherits(child, parent) {
for (var key in parent) {
if (Object.prototype.hasOwnProperty.call(parent, key)) {
child[key] = parent[key];
function inherits(ctor, superCtor) {
ctor.super_ = ctor.__super__ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
});
}
exports.inherits = inherits;
......@@ -305,23 +300,23 @@ exports.isWebPage = isWebPage;
/**
* Object recursive merging utility.
*
* @param Object obj1 the destination object
* @param Object obj2 the source object
* @param Object origin the origin object
* @param Object add the object to merge data into origin
* @return Object
*/
function mergeObjects(obj1, obj2) {
for (var p in obj2) {
function mergeObjects(origin, add) {
for (var p in add) {
try {
if (obj2[p].constructor === Object) {
obj1[p] = mergeObjects(obj1[p], obj2[p]);
if (add[p].constructor === Object) {
origin[p] = mergeObjects(origin[p], add[p]);
} else {
obj1[p] = obj2[p];
origin[p] = add[p];
}
} catch(e) {
obj1[p] = obj2[p];
origin[p] = add[p];
}
}
return obj1;
return origin;
}
exports.mergeObjects = mergeObjects;
......
{
"name": "casperjs",
"description": "Navigation scripting & testing utility for PhantomJS",
"version": "0.6.8",
"version": "0.6.10",
"keywords": [
"phantomjs",
"javascript"
......@@ -14,7 +14,7 @@
}
],
"dependencies": {
"http://www.phantomjs.org/": "1.3"
"http://www.phantomjs.org/": "1.5"
},
"bugs": {
"url": "https://github.com/n1k0/casperjs/issues"
......
casper = require("casper").create()
### listening to a custom event ###
# listening to a custom event
casper.on "google.loaded", (title) ->
casper.echo "Google page title is #{title}"
@echo "Google page title is #{title}"
casper.start "http://google.com/", ->
### emitting a custom event ###
# emitting a custom event
@emit "google.loaded", @getTitle()
casper.run()
......
var casper;
var casper = require("casper").create();
casper = require("casper").create();
/* listening to a custom event */
// listening to a custom event
casper.on("google.loaded", function(title) {
casper.echo("Google page title is " + title);
this.echo("Google page title is " + title);
});
casper.start("http://google.com/", function() {
/* emitting a custom event */
// emitting a custom event
this.emit("google.loaded", this.getTitle());
});
......
###
A basic custom logging implementation. The idea is to (extremely) verbosely log
every received resource.
A basic custom logging implementation. The idea is to (extremely) verbosely
log every received resource.
###
casper = require("casper").create
###
Every time a resource is received, a new log entry is added to the stack at
the 'verbose' level.
Every time a resource is received, a new log entry is added to the stack
at the 'verbose' level.
@param Object resource A phantomjs resource object
###
onResourceReceived: (self, resource) ->
infos = []
......@@ -23,9 +25,9 @@ casper = require("casper").create
verbose: true # we want to see the log printed out to the console
logLevel: "verbose" # of course we want to see logs to our new level :)
### add a new 'verbose' logging level at the lowest priority ###
# add a new 'verbose' logging level at the lowest priority
casper.logLevels = ["verbose"].concat casper.logLevels
### test our new logger with google ###
casper.start "http://www.google.com/"
casper.run()
# test our new logger with google
casper.start("http://www.google.com/").run ->
@exit()
......
/*
A basic custom logging implementation. The idea is to (extremely) verbosely log
every received resource.
*/
var casper;
* A basic custom logging implementation. The idea is to (extremely) verbosely
* log every received resource.
*/
casper = require("casper").create({
var casper = require("casper").create({
/*
Every time a resource is received, a new log entry is added to the stack at
the 'verbose' level.
......@@ -38,7 +36,7 @@ casper = require("casper").create({
/* add a new 'verbose' logging level at the lowest priority */
casper.logLevels = ["verbose"].concat(casper.logLevels);
/* test our new logger with google */
casper.start("http://www.google.com/");
casper.run();
\ No newline at end of file
// test our new logger with google
casper.start("http://www.google.com/").run(function() {
this.exit();
});
......
###
Download the google logo image as base64
Download the google logo image onto the local filesystem
###
casper = require("casper").create verbose: true
casper = require("casper").create()
casper.start "http://www.google.fr/", ->
@echo @base64encode "http://www.google.fr/images/srpr/logo3w.png"
@echo @download "http://www.google.fr/images/srpr/logo3w.png", "logo.png"
casper.run()
......
/*
Download the google logo image as base64
*/
* download the google logo image onto the local filesystem
*/
var casper;
casper = require("casper").create({
verbose: true
});
var casper = require("casper").create();
casper.start("http://www.google.fr/", function() {
this.echo(this.base64encode("http://www.google.fr/images/srpr/logo3w.png"));
this.download("http://www.google.fr/images/srpr/logo3w.png", "logo.png");
});
casper.run();
......
......@@ -29,21 +29,18 @@ links = [
"http://bing.com/"
];
/* Just opens the page and prints the title */
start = function(link) {
// Just opens the page and prints the title
var start = function(link) {
this.start(link, function() {
this.echo("Page title: " + this.getTitle());
this.echo('Page title: ' + this.getTitle());
});
};
/*
Get the links, and add them to the links array
(It could be done all in one step, but it is intentionally splitted)
*/
addLinks = function(link) {
// Get the links, and add them to the links array
// (It could be done all in one step, but it is intentionally splitted)
var addLinks = function(link) {
this.then(function() {
var found;
found = this.evaluate(searchLinks);
var found = this.evaluate(searchLinks);
this.echo(found.length + " links found on " + link);
links = links.concat(found);
});
......@@ -57,10 +54,10 @@ casper.then(function() {
currentLink = 0;
/* As long as it has a next link, and is under the maximum limit, will keep running */
check = function() {
// As long as it has a next link, and is under the maximum limit, will keep running
function check() {
if (links[currentLink] && currentLink < upTo) {
this.echo("--- Link " + currentLink + " ---");
this.echo('--- Link ' + currentLink + ' ---');
start.call(this, links[currentLink]);
addLinks.call(this, links[currentLink]);
currentLink++;
......
getLinks = ->
links = document.querySelectorAll "h3.r a"
Array::map.call links, (e) -> e.getAttribute "href"
links = []
casper = require("casper").create()
links = document.querySelectorAll("h3.r a")
Array::map.call links, (e) ->
e.getAttribute "href"
casper.start "http://google.fr/", ->
### search for 'casperjs' from google form ###
@fill 'form[action="/search"]', q: "casperjs", true
# search for 'casperjs' from google form
@fill "form[action=\"/search\"]", q: "casperjs" , true
casper.then ->
### aggregate results for the 'casperjs' search ###
links = @evaluate getLinks
### search for 'phantomjs' from google form ###
@fill 'form[action="/search"]', q: "phantomjs", true
# aggregate results for the 'casperjs' search
links = @evaluate(getLinks)
# now search for 'phantomjs' by fillin the form again
@fill "form[action=\"/search\"]", q: "phantomjs" , true
casper.then ->
### concat results for the 'phantomjs' search ###
links = links.concat @evaluate(getLinks)
# aggregate results for the 'phantomjs' search
links = links.concat(@evaluate(getLinks))
casper.run ->
### display results ###
@echo "#{links.length} links found:"
@echo " - " + links.join "\n - "
# echo results in some pretty fashion
@echo links.length + " links found:"
@echo(" - " + links.join("\n - "))
@exit()
......
var casper, getLinks, links;
getLinks = function() {
var links;
links = document.querySelectorAll("h3.r a");
var links = document.querySelectorAll("h3.r a");
return Array.prototype.map.call(links, function(e) {
return e.getAttribute("href");
});
};
links = [];
casper = require("casper").create();
casper.start("http://google.fr/", function() {
/* search for 'casperjs' from google form */
this.fill('form[action="/search"]', {
q: "casperjs"
}, true);
// search for 'casperjs' from google form
this.fill('form[action="/search"]', { q: "casperjs" }, true);
});
casper.then(function() {
/* aggregate results for the 'casperjs' search */
// aggregate results for the 'casperjs' search
links = this.evaluate(getLinks);
/* search for 'phantomjs' from google form */
this.fill('form[action="/search"]', {
q: "phantomjs"
}, true);
// now search for 'phantomjs' by fillin the form again
this.fill('form[action="/search"]', { q: "phantomjs" }, true);
});
casper.then(function() {
/* concat results for the 'phantomjs' search */
// aggregate results for the 'phantomjs' search
links = links.concat(this.evaluate(getLinks));
});
casper.run(function() {
/* display results */
// echo results in some pretty fashion
this.echo(links.length + " links found:");
this.echo(" - " + links.join("\n - "));
this.echo(" - " + links.join("\n - "))
this.exit();
});
......
......@@ -15,7 +15,7 @@ casper = require("casper").create verbose: true
casper.fetchScore = ->
@evaluate ->
result = document.querySelector('#resultStats').innerText
~~(/Environ ([0-9\s]{1,}).*/.exec(result)[1].replace(/\s/g, ''))
parseInt /Environ ([0-9\s]{1,}).*/.exec(result)[1].replace(/\s/g, '')
terms = casper.cli.args # terms are passed through command-line arguments
......@@ -35,10 +35,13 @@ casper.each terms, (self, term) ->
@then ->
score = @fetchScore()
scores.push term: term, score: score
self.echo "#{term}: #{score}"
@echo "#{term}: #{score}"
casper.run ->
scores.sort (a, b) -> b.score - a.score;
if scores.length is 0
@echo "No result found"
else
scores.sort (a, b) -> b.score - a.score
winner = scores[0]
@echo "Winner is \"#{winner.term}\" with #{winner.score} results"
@echo "Winner is \"" + winner.term + "\" with " + winner.score + " results"
@exit()
......
/*
Takes provided terms passed as arguments and query google for the number of
estimated results each have.
Usage:
$ casperjs googlematch.js nicolas chuck borris
nicolas: 69600000
chuck: 49500000
borris: 2370000
winner is "nicolas" with 69600000 results
*/
* Takes provided terms passed as arguments and query google for the number of
* estimated results each have.
*
* Usage:
* $ casperjs googlematch.js nicolas chuck borris
* nicolas: 69600000
* chuck: 49500000
* borris: 2370000
* winner is "nicolas" with 69600000 results
*/
var casper, scores, terms;
......@@ -18,9 +18,8 @@ casper = require("casper").create({
casper.fetchScore = function() {
return this.evaluate(function() {
var result;
result = document.querySelector('#resultStats').innerText;
return ~~(/Environ ([0-9\s]{1,}).*/.exec(result)[1].replace(/\s/g, ''));
var result = document.querySelector('#resultStats').innerText;
return parseInt(/Environ ([0-9\s]{1,}).*/.exec(result)[1].replace(/\s/g, ''), 10);
});
};
......@@ -39,29 +38,30 @@ casper.echo("Let the match begin between \"" + (terms.join('", "')) + "\"!");
casper.start("http://google.fr/");
casper.each(terms, function(self, term) {
casper.each(terms, function(casper, term, i) {
this.echo('Fecthing score for ' + term);
this.then(function() {
this.fill('form[action="/search"]', {
q: term
}, true);
this.fill('form[action="/search"]', {q: term}, true);
});
this.then(function() {
var score;
score = this.fetchScore();
var score = this.fetchScore();
scores.push({
term: term,
score: score
});
self.echo(term + ": " + score);
this.echo(term + ': ' + score);
});
});
casper.run(function() {
var winner;
if (scores.length === 0) {
this.echo("No result found");
} else {
scores.sort(function(a, b) {
return b.score - a.score;
});
winner = scores[0];
var winner = scores[0];
this.echo("Winner is \"" + winner.term + "\" with " + winner.score + " results");
}
this.exit();
});
......
failed = [];
failed = []
start = null
links = [
"http://google.com/"
"http://akei.com/"
"http://lemonde.fr/"
"http://liberation.fr/"
"http://cdiscount.fr/"
]
casper = require("casper").create
onStepTimeout: -> failed.push @requestUrl
onStepTimeout: ->
failed.push @requestUrl
@test.fail "#{@requestUrl} loads in less than #{timeout}ms."
links = [
'http://google.com/'
'http://akei.com/'
'http://lemonde.fr/'
'http://liberation.fr/'
'http://cdiscount.fr/'
]
casper.on "load.finished", ->
@echo "#{@requestUrl} loaded in #{new Date() - start}ms", "PARAMETER"
timeout = ~~casper.cli.get(0)
timeout = 1000 if timeout < 1
casper.options.stepTimeout = timeout
casper.echo "Testing with timeout=#{casper.options.stepTimeout}ms."
casper.echo "Testing with timeout=#{timeout}ms, please be patient."
casper.start()
casper.each links, (self, link) ->
@test.comment "Adding #{link} to test suite"
@thenOpen link, ->
if @requestUrl in failed
@test.fail "#{@requestUrl} loaded in less than #{timeout}ms."
else
@then ->
@test.comment "Loading #{link}"
start = new Date()
@open link
@then ->
if @requestUrl not in failed
@test.pass "#{@requestUrl} loaded in less than #{timeout}ms."
casper.run ->
......
var casper, failed, links, timeout,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
failed = [];
var failed = [];
var start = null;
var links = [
"http://google.com/'",
"http://akei.com/'",
"http://lemonde.fr/'",
"http://liberation.fr/'",
"http://cdiscount.fr/"
];
casper = require("casper").create({
var casper = require("casper").create({
onStepTimeout: function() {
failed.push(this.requestUrl);
this.test.fail(this.requestUrl + " loads in less than " + timeout + "ms.");
}
});
links = [
'http://google.com/',
'http://akei.com/',
'http://lemonde.fr/',
'http://liberation.fr/',
'http://cdiscount.fr/'
];
timeout = ~~casper.cli.get(0);
if (timeout < 1) {
timeout = 1000;
}
casper.on("load.finished", function() {
this.echo(this.requestUrl + " loaded in " + (new Date() - start) + "ms", "PARAMETER");
});
casper.options.stepTimeout = timeout;
var timeout = ~~casper.cli.get(0);
casper.options.stepTimeout = timeout > 0 ? timeout : 1000;
casper.echo("Testing with timeout=" + casper.options.stepTimeout + "ms.");
casper.echo("Testing with timeout=" + casper.options.stepTimeout + "ms, please be patient.");
casper.start();
casper.each(links, function(self, link) {
this.test.comment("Adding " + link + " to test suite");
this.thenOpen(link, function() {
var _ref;
if (_ref = this.requestUrl, __indexOf.call(failed, _ref) >= 0) {
this.test.fail("" + this.requestUrl + " loaded in less than " + timeout + "ms.");
} else {
this.test.pass("" + this.requestUrl + " loaded in less than " + timeout + "ms.");
casper.each(links, function(casper, link) {
this.then(function() {
this.test.comment("Loading " + link);
start = new Date();
this.open(link);
});
this.then(function() {
var message = this.requestUrl + " loads in less than " + timeout + "ms.";
if (failed.indexOf(this.requestUrl) === -1) {
this.test.pass(message);
}
});
});
......
......@@ -4,12 +4,15 @@ Just a silly game.
$ casperjs samples/timeout.js 500
Will google.com load in less than 500ms?
NOPE.
$ casperjs samples/timeout.js 1000
Will google.com load in less than 1000ms?
NOPE.
$ casperjs samples/timeout.js 1500
Will google.com load in less than 1500ms?
NOPE.
$ casperjs samples/timeout.js 2000
Will google.com load in less than 2000ms?
YES!
......
/*
Just a silly game.
$ casperjs samples/timeout.js 500
Will google.com load in less than 500ms?
NOPE.
$ casperjs samples/timeout.js 1000
Will google.com load in less than 1000ms?
NOPE.
$ casperjs samples/timeout.js 1500
Will google.com load in less than 1500ms?
NOPE.
$ casperjs samples/timeout.js 2000
Will google.com load in less than 2000ms?
YES!
*/
* Just a silly game.
*
* $ casperjs samples/timeout.js 500
* Will google.com load in less than 500ms?
* NOPE.
*
* $ casperjs samples/timeout.js 1000
* Will google.com load in less than 1000ms?
* NOPE.
*
* $ casperjs samples/timeout.js 1500
* Will google.com load in less than 1500ms?
* NOPE.
*
* $ casperjs samples/timeout.js 2000
* Will google.com load in less than 2000ms?
* YES!
*/
var casper, timeout;
......@@ -39,7 +42,7 @@ casper.echo("Will google.com load in less than " + timeout + "ms?");
casper.options.timeout = timeout;
casper.start("http://www.google.com/", function() {
this.echo("YES!", 'GREEN_BAR');
this.echo("YES!", "GREEN_BAR");
this.exit();
});
......
......@@ -7,7 +7,7 @@ var fs = require('fs');
var utils = require('utils');
var f = utils.format;
var casper = require('casper').create({
faultTolerant: false
exitOnError: false
});
// Options from cli
......
......@@ -19,7 +19,7 @@
<input type="checkbox" name="checklist[]" value="1" />
<input type="checkbox" name="checklist[]" value="2" />
<input type="checkbox" name="checklist[]" value="3" />
<input type="submit"/>
<input type="submit" name="submit" value="submit" />
</form>
</body>
</html>
......
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CasperJS test mouse events</title>
</head>
<body>
<a id="test1" href="#" onmousedown="results.test1 = true;">test</a>
<a id="test2" href="#">test</a>
<a id="test3" href="#" onmouseup="results.test3 = true;">test</a>
<a id="test4" href="#">test</a>
<a id="test5" href="#" onmouseover="results.test5 = true;">test</a>
<a id="test6" href="#">test</a>
<a id="test7" href="#" onmouseout="results.test7 = true;">test</a>
<a id="test8" href="#">test</a>
<script>
(function(window) {
window.results = {
test1: false,
test2: false,
test3: false,
test4: false,
test5: false,
test6: false,
test7: false,
test8: false
};
document.querySelector('#test2').onmousedown = function(event) {
results.test2 = true;
event.preventDefault();
};
document.querySelector('#test4').onmouseup = function(event) {
results.test4 = true;
event.preventDefault();
};
document.querySelector('#test6').onmouseover = function(event) {
results.test6 = true;
event.preventDefault();
};
document.querySelector('#test8').onmouseout = function(event) {
results.test8 = true;
event.preventDefault();
};
})(window);
</script>
</body>
</html>
......@@ -12,16 +12,30 @@ casper.then(function() {
// onclick variants tests
casper.thenOpen('tests/site/click.html', function() {
this.test.comment('CasperUtils.click()');
this.test.assert(this.click('#test1'), 'CasperUtils.click() can click an `href="javascript:` link');
this.test.assert(this.click('#test2'), 'CasperUtils.click() can click an `href="#"` link');
this.test.assert(this.click('#test3'), 'CasperUtils.click() can click an `onclick=".*; return false"` link');
this.test.assert(this.click('#test4'), 'CasperUtils.click() can click an unobstrusive js handled link');
this.test.comment('Casper.click()');
this.test.assert(this.click('#test1'), 'Casper.click() can click an `href="javascript:` link');
this.test.assert(this.click('#test2'), 'Casper.click() can click an `href="#"` link');
this.test.assert(this.click('#test3'), 'Casper.click() can click an `onclick=".*; return false"` link');
this.test.assert(this.click('#test4'), 'Casper.click() can click an unobstrusive js handled link');
var results = this.getGlobal('results');
this.test.assert(results.test1, 'Casper.click() has clicked an `href="javascript:` link');
this.test.assert(results.test2, 'Casper.click() has clicked an `href="#"` link');
this.test.assert(results.test3, 'Casper.click() has clicked an `onclick=".*; return false"` link');
this.test.assert(results.test4, 'Casper.click() has clicked an unobstrusive js handled link');
});
// clickLabel tests
casper.thenOpen('tests/site/click.html', function() {
this.test.comment('Casper.clickLabel()');
this.test.assert(this.clickLabel('test1'), 'Casper.clickLabel() can click an `href="javascript:` link');
this.test.assert(this.clickLabel('test2'), 'Casper.clickLabel() can click an `href="#"` link');
this.test.assert(this.clickLabel('test3'), 'Casper.clickLabel() can click an `onclick=".*; return false"` link');
this.test.assert(this.clickLabel('test4'), 'Casper.clickLabel() can click an unobstrusive js handled link');
var results = this.getGlobal('results');
this.test.assert(results.test1, 'CasperUtils.click() has clicked an `href="javascript:` link');
this.test.assert(results.test2, 'CasperUtils.click() has clicked an `href="#"` link');
this.test.assert(results.test3, 'CasperUtils.click() has clicked an `onclick=".*; return false"` link');
this.test.assert(results.test4, 'CasperUtils.click() has clicked an unobstrusive js handled link');
this.test.assert(results.test1, 'Casper.clickLabel() has clicked an `href="javascript:` link');
this.test.assert(results.test2, 'Casper.clickLabel() has clicked an `href="#"` link');
this.test.assert(results.test3, 'Casper.clickLabel() has clicked an `onclick=".*; return false"` link');
this.test.assert(results.test4, 'Casper.clickLabel() has clicked an unobstrusive js handled link');
});
// casper.mouse
......
casper.start('tests/site/mouse-events.html');
casper.then(function() {
this.test.comment('CasperUtils.mouseEvent()');
this.test.assert(this.mouseEvent('mousedown', '#test1'), 'CasperUtils.mouseEvent() can dispatch a mousedown event');
this.test.assert(this.mouseEvent('mousedown', '#test2'), 'CasperUtils.mouseEvent() can dispatch a mousedown event handled by unobstrusive js');
this.test.assert(this.mouseEvent('mouseup', '#test3'), 'CasperUtils.mouseEvent() can dispatch a mouseup event');
this.test.assert(this.mouseEvent('mouseup', '#test4'), 'CasperUtils.mouseEvent() can dispatch a mouseup event handled by unobstrusive js');
this.test.assert(this.mouseEvent('mouseover', '#test5'), 'CasperUtils.mouseEvent() can dispatch a mouseover event');
this.test.assert(this.mouseEvent('mouseover', '#test6'), 'CasperUtils.mouseEvent() can dispatch a mouseover event handled by unobstrusive js');
this.test.assert(this.mouseEvent('mouseout', '#test7'), 'CasperUtils.mouseEvent() can dispatch a mouseout event');
this.test.assert(this.mouseEvent('mouseout', '#test8'), 'CasperUtils.mouseEvent() can dispatch a mouseout event handled by unobstrusive js');
var results = this.getGlobal('results');
this.test.assert(results.test1, 'CasperUtils.mouseEvent() triggered mousedown');
this.test.assert(results.test2, 'CasperUtils.mouseEvent() triggered mousedown via unobstrusive js');
this.test.assert(results.test3, 'CasperUtils.mouseEvent() triggered mouseup');
this.test.assert(results.test4, 'CasperUtils.mouseEvent() triggered mouseup via unobstrusive js');
this.test.assert(results.test5, 'CasperUtils.mouseEvent() triggered mouseover');
this.test.assert(results.test6, 'CasperUtils.mouseEvent() triggered mouseover via unobstrusive js');
this.test.assert(results.test7, 'CasperUtils.mouseEvent() triggered mouseout');
this.test.assert(results.test8, 'CasperUtils.mouseEvent() triggered mouseout via unobstrusive js');
});
casper.run(function() {
this.test.done();
});
......@@ -19,6 +19,11 @@ function Plip() { return 'plop'; }
function foo_bar(boz) {}
var gni = function ($bubu_bibi, __popo__) {};
var gno = function ( arg1, /*plop*/ arg2 ) { };
function issue129(term) {
// see issue #129
return term;
// see issue #129
}
t.assertEquals(injector.extract(Plop), {
name: 'Plop',
args: ['foo', 'bar'],
......@@ -52,4 +57,8 @@ eval('processed = ' + injector.process({ a: 1, b: 2 }));
t.assertType(processed, "function", 'FunctionArgsInjector.process() processed a function');
t.assertEquals(processed(), 3, 'FunctionArgsInjector.process() processed the function correctly');
// Issue #129
var fnIssue129 = createInjector(issue129).process({term: 'fixed'});
t.assertEquals(fnIssue129('fixed'), 'fixed', 'FunctionArgsInjector.process() has issue #129 fixed');
t.done();
......
......@@ -2,6 +2,8 @@ var fs = require('fs');
var t = casper.test;
casper.start();
t.comment('Tester.testEquals()');
t.assert(t.testEquals(null, null), 'Tester.testEquals() null equality');
t.assertNot(t.testEquals(null, undefined), 'Tester.testEquals() null vs. undefined inequality');
......@@ -26,8 +28,6 @@ t.assertNot(t.testEquals({1:{name:"bob",age:28}, 2:{name:"john",age:26}}, {1:{na
t.assert(t.testEquals(function(x){return x;}, function(x){return x;}), 'Tester.testEquals() function equality');
t.assertNot(t.testEquals(function(x){return x;}, function(y){return y+2;}), 'Tester.testEquals() function inequality');
t.assertNotEquals(42, 43, 'Tester.assertNotEquals() works as expected');
t.comment('Tester.sortFiles()');
var testDirRoot = fs.pathJoin(phantom.casperPath, 'tests', 'testdir');
var files = t.findTestFiles(testDirRoot);
......@@ -44,11 +44,65 @@ var expected = [
});
t.assertEquals(files, expected, 'findTestFiles() find test files and sort them');
t.comment('Tester.assertTextExists()');
casper.start('tests/site/index.html', function() {
casper.thenOpen('tests/site/index.html', function() {
t.comment('Tester.assertTextExists()');
t.assertTextExists('form', 'Tester.assertTextExists() checks that page body contains text');
});
casper.then(function() {
t.comment('Tester.assert()');
t.assert(true, 'Tester.assert() works as expected');
t.comment('Tester.assertNot()');
t.assertNot(false, 'Tester.assertNot() works as expected');
t.comment('Tester.assertEquals()');
t.assertEquals(true, true, 'Tester.assertEquals() works as expected');
t.comment('Tester.assertNotEquals()');
t.assertNotEquals(true, false, 'Tester.assertNotEquals() works as expected');
t.comment('Tester.assertEval()');
t.assertEval(function() {
return true;
}, 'Tester.assertEval() works as expected');
t.comment('Tester.assertEvalEquals()');
t.assertEvalEquals(function() {
return 42;
}, 42, 'Tester.assertEvalEquals() works as expected');
t.comment('Tester.assertExists()');
t.assertExists('body', 'Tester.assertExists() works as expected');
t.comment('Tester.assertDoesntExist()');
t.assertDoesntExist('foobar', 'Tester.assertDoesntExist() works as expected');
t.comment('Tester.assertHttpStatus()');
// using file:// protocol, HTTP status is always null
t.assertHttpStatus(null, 'Tester.assertHttpStatus() works as expected');
t.comment('Tester.assertMatch()');
t.assertMatch("the lazy dog", /lazy/, 'Tester.assertMatch() works as expected');
t.comment('Tester.assertRaises()');
t.assertRaises(function() {
throw new Error('plop');
}, [], 'Tester.assertRaises() works as expected');
t.comment('Tester.assertResourceExists()');
t.assertResourceExists(/index\.html/, 'Tester.assertResourceExists() works as expected');
t.comment('Tester.assertTitle()');
t.assertTitle('CasperJS test index', 'Tester.assertTitle() works as expected');
t.comment('Tester.assertType()');
t.assertType("plop", "string", "Tester.assertType() works as expected");
t.comment('Tester.assertUrlMatch()');
t.assertUrlMatch(/index\.html$/, "Tester.assertUrlMatch() works as expected");
});
casper.run(function() {
t.done();
});
......