kekrozsak/vendor/swiftmailer/swiftmailer/test-suite/sweety.js

472 lines
12 KiB
JavaScript

/*
JavaScript wrapper around REST API in Sweety.
*/
/**
* A convenience class for using XPath.
* @author Chris Corbyn
* @constructor
*/
function SweetyXpath() {
/**
* Get the first node matching the given expression.
* @param {String} expr
* @param {Element} node
* @returns Element
*/
this.getFirstNode = function getFirstNode(expr, node) {
var firstNode = _getRootNode(node).evaluate(
expr, node, _getNsResolver(node), XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return firstNode.singleNodeValue;
},
/**
* Get all nodes matching the given expression.
* The returned result is a Node Snapshot.
* @param {String} expr
* @param {Element} node
* @returns Element[]
*/
this.getNodes = function getNodes(expr, node) {
var nodes = _getRootNode(node).evaluate(
expr, node, _getNsResolver(node), XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
var nodeSet = new Array();
for (var i = 0, len = nodes.snapshotLength; i < len; i++) {
nodeSet.push(nodes.snapshotItem(i));
}
return nodeSet;
},
/**
* Get the string value of the node matching the given expression.
* @param {String} expr
* @param {Element} node
* @returns String
*/
this.getValue = function getValue(expr, node) {
return _getRootNode(node).evaluate(
expr, node, _getNsResolver(node), XPathResult.STRING_TYPE, null).stringValue;
}
/**
* Get the root node from which run evaluate.
* @param {Element} node
* @returns Element
*/
var _getRootNode = function _getRootNode(node) {
if (node.ownerDocument && node.ownerDocument.evaluate) {
return node.ownerDocument;
} else {
if (node.evaluate) {
return node;
} else {
return document;
}
}
}
/**
* Get the NS Resolver used when searching.
* @param {Element} node
* @returns Element
*/
var _getNsResolver = function _getNsResolver(node) {
if (!document.createNSResolver) {
return null;
}
if (node.ownerDocument) {
return document.createNSResolver(node.ownerDocument.documentElement);
} else {
return document.createNSResolver(node.documentElement);
}
}
}
/**
* The reporter interface so Sweety can tell the UI what's happening.
* @author Chris Corbyn
* @constructor
*/
function SweetyReporter() { //Interface/Base Class
var _this = this;
/**
* Create a sub-reporter for an individual test case.
* @param {String} testCaseName
* @returns SweetyReporter
*/
this.getReporterFor = function getReporterFor(testCaseName) {
return _this;
}
/**
* Start reporting.
*/
this.start = function start() {
}
/**
* Handle a skipped test case.
* @param {String} message
* @param {String} path
*/
this.reportSkip = function reportSkip(message, path) {
}
/**
* Handle a passing assertion.
* @param {String} message
* @param {String} path
*/
this.reportPass = function reportPass(message, path) {
}
/**
* Handle a failing assertion.
* @param {String} message
* @param {String} path
*/
this.reportFail = function reportFail(message, path) {
}
/**
* Handle an unexpected exception.
* @param {String} message
* @param {String} path
*/
this.reportException = function reportException(message, path) {
}
/**
* Handle miscellaneous test output.
* @param {String} output
* @param {String} path
*/
this.reportOutput = function reportOutput(output, path) {
}
/**
* Finish reporting.
*/
this.finish = function finish() {
}
}
/**
* Represents a single test case being run.
* @author Chris Corbyn
* @constructor
*/
function SweetyTestCaseRun(testClass, reporter) {
var _this = this;
/** The XMLHttpRequest used in testing */
var _req;
/** XPath handler */
var _xpath = new SweetyXpath();
/** Callback function for completion event */
this.oncompletion = function oncompletion() {
}
/**
* Run this test.
*/
this.run = function run() {
if (!reporter.isStarted()) {
reporter.start();
}
_req = _createHttpRequest();
if (!_req) {
return;
}
_req.open("GET", "?test=" + testClass + "&format=xml", true);
_req.onreadystatechange = _handleXml;
_req.send(null);
}
/**
* Get an XmlHttpRequest instance, cross browser compatible.
* @return Object
*/
var _createHttpRequest = function _createHttpRequest() {
var req = false;
if (window.XMLHttpRequest && !(window.ActiveXObject)) {
try {
req = new XMLHttpRequest();
} catch(e) {
req = false;
}
} else if (window.ActiveXObject) {
try {
req = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
req = false;
}
}
}
return req;
}
/**
* Handle the XML response from the test.
*/
var _handleXml = function _handleXml() {
if (_req.readyState == 4) {
try {
var xml = _req.responseXML;
var txt = _req.responseText.replace(/[\r\n]+/g, "").
replace(/^(.+)<\?xml.*$/, "$1");
//Test case was skipped
var skipElements = xml.getElementsByTagName('skip');
if (!skipElements || 1 != skipElements.length)
{
var runElements = xml.getElementsByTagName('run');
//Invalid document, an error probably occured
if (!runElements || 1 != runElements.length) {
reporter.reportException(
"Invalid XML response: " +
_stripTags(txt.replace(/^\s*<\?xml.+<\/(?:name|pass|fail|exception)>/g, "")), testClass);
} else {
var everything = runElements.item(0);
_parseResults(everything, testClass);
reporter.finish();
}
}
else
{
reporter.reportSkip(_textValueOf(skipElements.item(0)), testClass);
reporter.finish();
}
} catch (ex) {
//Invalid document or an error occurred.
reporter.reportException(
"Invalid XML response: " +
_stripTags(txt.replace(/^\s*<\?xml.+<\/(?:name|pass|fail|exception)>/g, "")), testClass);
}
//Invoke the callback
_this.oncompletion();
}
}
/**
* Cross browser method for reading the value of a node in XML.
* @param {Element} node
* @returns String
*/
var _textValueOf = function _textValueOf(node) {
if (!node.textContent && node.text) {
return node.text;
} else {
return node.textContent;
}
}
var _stripTags = function _stripTags(txt) {
txt = txt.replace(/[\r\n]+/g, "");
return txt.replace(
/<\/?(?:a|b|br|p|strong|u|i|em|span|div|ul|ol|li|table|thead|tbody|th|td|tr)\b.*?\/?>/g,
"");
}
/**
* Parse an arbitrary message output.
* @param {Element} node
* @param {String} path
*/
var _parseMessage = function _parseMessage(node, path) {
reporter.reportOutput(_textValueOf(node), path);
}
/**
* Parse formatted text output (such as a dump()).
* @param {Element} node
* @param {String} path
*/
var _parseFormatted = function _parseFormatted(node, path) {
reporter.reportOutput(_textValueOf(node), path);
}
/**
* Parse failing test assertion.
* @param {Element} node
* @param {String} path
*/
var _parseFail = function _parseFail(node, path) {
reporter.reportFail(_textValueOf(node), path);
}
/**
* Parse an Exception.
* @param {Element} node
* @param {String} path
*/
var _parseException = function _parseException(node, path) {
reporter.reportException(_textValueOf(node), path);
}
/**
* Parse passing test assertion.
* @param {Element} node
* @param {String} path
*/
var _parsePass = function _parsePass(node, path) {
reporter.reportPass(_textValueOf(node), path);
}
/**
* Parse an entire test case
* @param {Element} node
* @param {String} path
*/
var _parseTestCase = function _parseTestCase(node, path) {
var testMethodNodes = _xpath.getNodes("./test", node);
for (var x in testMethodNodes) {
var testMethodNode = testMethodNodes[x];
var testMethodName = _xpath.getValue("./name", testMethodNode);
var formattedNodes = _xpath.getNodes("./formatted", testMethodNode);
for (var i in formattedNodes) {
var formattedNode = formattedNodes[i];
_parseFormatted(formattedNode, path + " -> " + testMethodName);
}
var messageNodes = _xpath.getNodes("./message", testMethodNode);
for (var i in messageNodes) {
var messageNode = messageNodes[i];
_parseMessage(messageNode, path + " -> " + testMethodName);
}
var failNodes = _xpath.getNodes("./fail", testMethodNode);
for (var i in failNodes) {
var failNode = failNodes[i];
_parseFail(failNode, path + " -> " + testMethodName);
}
var exceptionNodes = _xpath.getNodes("./exception", testMethodNode);
for (var i in exceptionNodes) {
var exceptionNode = exceptionNodes[i];
_parseException(exceptionNode, path + " -> " + testMethodName);
}
var passNodes = _xpath.getNodes("./pass", testMethodNode);
for (var i in passNodes) {
var passNode = passNodes[i];
_parsePass(passNode, path + " -> " + testMethodName);
}
}
}
/**
* Parse an entire grouped or single test case.
* @param {Element} node
* @param {String} path
*/
var _parseResults = function _parseResults(node, path) {
var groupNodes = _xpath.getNodes("./group", node);
if (0 != groupNodes.length) {
for (var i in groupNodes) {
var groupNode = groupNodes[i];
var groupName = _xpath.getValue("./name", groupNode);
_parseResults(groupNode, path + " -> " + groupName);
}
} else {
var caseNodes = _xpath.getNodes("./case", node);
for (var i in caseNodes) {
var caseNode = caseNodes[i];
_parseTestCase(caseNode, path);
}
}
}
}
/**
* Runs a list of test cases.
* @author Chris Corbyn
* @constructor
*/
function SweetyTestRunner() {
var _this = this;
SweetyTestRunner._currentInstance = _this;
/** True if the test runner has been stopped */
var _cancelled = false;
/**
* Invoked to cause the test runner to stop execution at the next available
* opportunity. If XML is being parsed in another thread, or an AJAX request
* is in progress the test runner will wait until the next test.
* @param {Boolean} cancel
*/
this.cancelTesting = function cancelTesting(cancel) {
_cancelled = cancel;
}
/**
* Run the given list of test cases.
* @param {String[]} tests
* @param {SweetyReporter} reporter
*/
this.runTests = function runTests(tests, reporter) {
if (!reporter.isStarted()) {
reporter.start();
}
if (_cancelled || !tests || !tests.length) {
_cancelled = false;
reporter.finish();
return;
}
var testCase = tests.shift();
var caseReporter = reporter.getReporterFor(testCase);
var testRun = new SweetyTestCaseRun(testCase, caseReporter);
//Repeat until no tests remaining in list
// Ok, I know, I know I'll try to eradicate this lazy use of recursion
testRun.oncompletion = function() {
_this.runTests(tests, reporter);
};
testRun.run();
}
}
/** Active instance */
SweetyTestRunner._currentInstance = null;
/**
* Fetches the currently running instance of the TestRunner.
* @returns SweetyTestRunner
*/
SweetyTestRunner.getCurrentInstance = function getCurrentInstance() {
return this._currentInstance;
}