Commit c8a534cd c8a534cd131a9de8a1a7b2117ddaded3386640a3 by Nicolas Perriault

refs #26 - code modularization

1 parent 83ca92a6
/*!
* Casper is a navigation utility for PhantomJS.
*
* Documentation: http://n1k0.github.com/casperjs/
* Repository: http://github.com/n1k0/casperjs
*
* Copyright (c) 2011 Nicolas Perriault
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
(function(phantom){
/**
* This is a port of lime colorizer.
* http://trac.symfony-project.org/browser/tools/lime/trunk/lib/lime.php)
*
* (c) Fabien Potencier, Symfony project, MIT license
*/
phantom.Casper.Colorizer = function() {
var options = { bold: 1, underscore: 4, blink: 5, reverse: 7, conceal: 8 };
var foreground = { black: 30, red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37 };
var background = { black: 40, red: 41, green: 42, yellow: 43, blue: 44, magenta: 45, cyan: 46, white: 47 };
var styles = {
'ERROR': { bg: 'red', fg: 'white', bold: true },
'INFO': { fg: 'green', bold: true },
'TRACE': { fg: 'green', bold: true },
'PARAMETER': { fg: 'cyan' },
'COMMENT': { fg: 'yellow' },
'WARNING': { fg: 'red', bold: true },
'GREEN_BAR': { fg: 'white', bg: 'green', bold: true },
'RED_BAR': { fg: 'white', bg: 'red', bold: true },
'INFO_BAR': { fg: 'cyan', bold: true }
};
/**
* Adds a style to provided text.
*
* @params String text
* @params String styleName
* @return String
*/
this.colorize = function(text, styleName) {
if (styleName in styles) {
return this.format(text, styles[styleName]);
}
return text;
};
/**
* Formats a text using a style declaration object.
*
* @param String text
* @param Object style
* @return String
*/
this.format = function(text, style) {
if (typeof style !== "object") {
return text;
}
var codes = [];
if (style.fg && foreground[style.fg]) {
codes.push(foreground[style.fg]);
}
if (style.bg && background[style.bg]) {
codes.push(background[style.bg]);
}
for (var option in options) {
if (style[option] === true) {
codes.push(options[option]);
}
}
return "\033[" + codes.join(';') + 'm' + text + "\033[0m";
};
};
})(phantom);
\ No newline at end of file
/*!
* Casper is a navigation utility for PhantomJS.
*
* Documentation: http://n1k0.github.com/casperjs/
* Repository: http://github.com/n1k0/casperjs
*
* Copyright (c) 2011 Nicolas Perriault
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
(function(phantom) {
/**
* Function argument injector.
*
*/
phantom.Casper.FunctionArgsInjector = function(fn) {
if (!isType(fn, "function")) {
throw "FunctionArgsInjector() can only process functions";
}
this.fn = fn;
this.extract = function(fn) {
var match = /^function\s?(\w+)?\s?\((.*)\)\s?\{([\s\S]*)\}/i.exec(fn.toString().trim());
if (match && match.length > 1) {
var args = match[2].split(',').map(function(arg) {
return arg.replace(new RegExp(/\/\*+.*\*\//ig), "").trim();
}).filter(function(arg) {
return arg;
}) || [];
return {
name: match[1] ? match[1].trim() : null,
args: args,
body: match[3] ? match[3].trim() : ''
};
}
};
this.process = function(values) {
var fnObj = this.extract(this.fn);
if (!isType(fnObj, "object")) {
throw "Unable to process function " + this.fn.toString();
}
var inject = this.getArgsInjectionString(fnObj.args, values);
return 'function ' + (fnObj.name || '') + '(){' + inject + fnObj.body + '}';
};
this.getArgsInjectionString = function(args, values) {
values = typeof values === "object" ? values : {};
var jsonValues = escape(encodeURIComponent(JSON.stringify(values)));
var inject = [
'var __casper_params__ = JSON.parse(decodeURIComponent(unescape(\'' + jsonValues + '\')));'
];
args.forEach(function(arg) {
if (arg in values) {
inject.push('var ' + arg + '=__casper_params__["' + arg + '"];');
}
});
return inject.join('\n') + '\n';
};
};
})(phantom);
\ No newline at end of file
/*!
* Casper is a navigation utility for PhantomJS.
*
* Documentation: http://n1k0.github.com/casperjs/
* Repository: http://github.com/n1k0/casperjs
*
* Copyright (c) 2011 Nicolas Perriault
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
/**
* Provides a better typeof operator equivalent, able to retrieve the array
* type.
*
* @param mixed input
* @return String
* @see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
*/
function betterTypeOf(input) {
try {
return Object.prototype.toString.call(input).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
} catch (e) {
return typeof input;
}
}
/**
* Creates a new WebPage instance for Casper use.
*
* @param Casper casper A Casper instance
* @return WebPage
*/
function createPage(casper) {
var page;
if (phantom.version.major <= 1 && phantom.version.minor < 3 && isType(require, "function")) {
page = new WebPage();
} else {
page = require('webpage').create();
}
page.onAlert = function(message) {
casper.log('[alert] ' + message, "info", "remote");
if (isType(casper.options.onAlert, "function")) {
casper.options.onAlert.call(casper, casper, message);
}
};
page.onConsoleMessage = function(msg) {
var level = "info", test = /^\[casper:(\w+)\]\s?(.*)/.exec(msg);
if (test && test.length === 3) {
level = test[1];
msg = test[2];
}
casper.log(msg, level, "remote");
};
page.onLoadStarted = function() {
casper.loadInProgress = true;
};
page.onLoadFinished = function(status) {
if (status !== "success") {
var message = 'Loading resource failed with status=' + status;
if (casper.currentHTTPStatus) {
message += ' (HTTP ' + casper.currentHTTPStatus + ')';
}
message += ': ' + casper.requestUrl;
casper.log(message, "warning");
if (isType(casper.options.onLoadError, "function")) {
casper.options.onLoadError.call(casper, casper, casper.requestUrl, status);
}
}
if (casper.options.clientScripts) {
if (betterTypeOf(casper.options.clientScripts) !== "array") {
casper.log("The clientScripts option must be an array", "error");
} else {
for (var i = 0; i < casper.options.clientScripts.length; i++) {
var script = casper.options.clientScripts[i];
if (casper.page.injectJs(script)) {
casper.log('Automatically injected ' + script + ' client side', "debug");
} else {
casper.log('Failed injecting ' + script + ' client side', "warning");
}
}
}
}
// Client-side utils injection
var injected = page.evaluate(replaceFunctionPlaceholders(function() {
eval("var ClientUtils = " + decodeURIComponent("%utils%"));
__utils__ = new ClientUtils();
return __utils__ instanceof ClientUtils;
}, {
utils: encodeURIComponent(phantom.Casper.ClientUtils.toString())
}));
if (!injected) {
casper.log("Failed to inject Casper client-side utilities!", "warning");
} else {
casper.log("Successfully injected Casper client-side utilities", "debug");
}
// history
casper.history.push(casper.getCurrentUrl());
casper.loadInProgress = false;
};
page.onResourceReceived = function(resource) {
if (isType(casper.options.onResourceReceived, "function")) {
casper.options.onResourceReceived.call(casper, casper, resource);
}
if (resource.url === casper.requestUrl && resource.stage === "start") {
casper.currentHTTPStatus = resource.status;
if (isType(casper.options.httpStatusHandlers, "object") && resource.status in casper.options.httpStatusHandlers) {
casper.options.httpStatusHandlers[resource.status](casper, resource);
}
casper.currentUrl = resource.url;
}
};
page.onResourceRequested = function(request) {
if (isType(casper.options.onResourceRequested, "function")) {
casper.options.onResourceRequested.call(casper, casper, request);
}
};
return page;
}
/**
* Shorthands for checking if a value is of the given type. Can check for
* arrays.
*
* @param mixed what The value to check
* @param String typeName The type name ("string", "number", "function", etc.)
* @return Boolean
*/
function isType(what, typeName) {
return betterTypeOf(what) === typeName;
}
/**
* Checks if the provided var is a WebPage instance
*
* @param mixed what
* @return Boolean
*/
function isWebPage(what) {
if (!what || !isType(what, "object")) {
return false;
}
if (phantom.version.major <= 1 && phantom.version.minor < 3 && isType(require, "function")) {
return what instanceof WebPage;
} else {
return what.toString().indexOf('WebPage(') === 0;
}
}
/**
* Object recursive merging utility.
*
* @param Object obj1 the destination object
* @param Object obj2 the source object
* @return Object
*/
function mergeObjects(obj1, obj2) {
for (var p in obj2) {
try {
if (obj2[p].constructor == Object) {
obj1[p] = mergeObjects(obj1[p], obj2[p]);
} else {
obj1[p] = obj2[p];
}
} catch(e) {
obj1[p] = obj2[p];
}
}
return obj1;
}
/**
* Replaces a function string contents with placeholders provided by an
* Object.
*
* @param Function fn The function
* @param Object replacements Object containing placeholder replacements
* @return String A function string representation
*/
function replaceFunctionPlaceholders(fn, replacements) {
if (replacements && isType(replacements, "object")) {
fn = fn.toString();
for (var placeholder in replacements) {
var match = '%' + placeholder + '%';
do {
fn = fn.replace(match, replacements[placeholder]);
} while(fn.indexOf(match) !== -1);
}
}
return fn;
}
/*!
* Casper is a navigation utility for PhantomJS.
*
* Documentation: http://n1k0.github.com/casperjs/
* Repository: http://github.com/n1k0/casperjs
*
* Copyright (c) 2011 Nicolas Perriault
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
(function(phantom) {
/**
* JUnit XML (xUnit) exporter for test results.
*
*/
phantom.Casper.XUnitExporter = function() {
var node = function(name, attributes) {
var node = document.createElement(name);
for (var attrName in attributes) {
var value = attributes[attrName];
if (attributes.hasOwnProperty(attrName) && isType(attrName, "string")) {
node.setAttribute(attrName, value);
}
}
return node;
};
var xml = node('testsuite');
xml.toString = function() {
return this.outerHTML; // ouch
};
/**
* Adds a successful test result
*
* @param String classname
* @param String name
*/
this.addSuccess = function(classname, name) {
xml.appendChild(node('testcase', {
classname: classname,
name: name
}));
};
/**
* Adds a failed test result
*
* @param String classname
* @param String name
* @param String message
* @param String type
*/
this.addFailure = function(classname, name, message, type) {
var fnode = node('testcase', {
classname: classname,
name: name
});
var failure = node('failure', {
type: type || "unknown"
});
failure.appendChild(document.createTextNode(message || "no message left"));
fnode.appendChild(failure);
xml.appendChild(fnode);
};
/**
* Retrieves generated XML object - actually an HTMLElement.
*
* @return HTMLElement
*/
this.getXML = function() {
return xml;
};
};
})(phantom);
\ No newline at end of file