123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- /*
- 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;
- }
|