diff --git a/examples/tests/htmlTests.js b/examples/tests/htmlTests.js index 4e4730c..f2344f4 100644 --- a/examples/tests/htmlTests.js +++ b/examples/tests/htmlTests.js @@ -4,8 +4,8 @@ exports.tests = { "Common HTML Tests" : { urlPattern: "\.html$", tests: { - "All HTML responses should have a statusCode of 200": function(response) { - "200".should.equal(response.statusCode) + "All HTML responses should have a statusCode of 200": function(spiderPayload) { + should.equal(spiderPayload.response.statusCode, 200) } } } diff --git a/lib/reporters/console.js b/lib/reporters/console.js new file mode 100644 index 0000000..689b317 --- /dev/null +++ b/lib/reporters/console.js @@ -0,0 +1,116 @@ +var exceptions = require("exceptions"); +var formatErrors = require("formaterrors"); +var failTheme = new formatErrors.StackTheme(); +failTheme.messageLineHighlights = [" "]; +failTheme.stackHighlights = [" "]; +failTheme.stackHighlightPatterns = ["at"]; + +var errorTheme = new formatErrors.StackTheme(); +errorTheme.messageLineHighlights = [" "]; +errorTheme.stackHighlights = [" "]; +errorTheme.stackHighlightPatterns = ["at"]; + +// todo consider making these options configurable as implied by the name +var options = { + "error_prefix": "\u001B[31m", + "error_suffix": "\u001B[39m", + "ok_prefix": "\u001B[32m", + "ok_suffix": "\u001B[39m", + "bold_prefix": "\u001B[1m", + "bold_suffix": "\u001B[22m", + "assertion_prefix": "\u001B[35m", + "assertion_suffix": "\u001B[39m" +}; + +exports.report = function(suites, Result) { + + var error = function (str) { + return options.error_prefix + str + options.error_suffix; + }; + var ok = function (str) { + return options.ok_prefix + str + options.ok_suffix; + }; + var bold = function (str) { + return options.bold_prefix + str + options.bold_suffix; + }; + var assertionMessage = function (str) { + return options.assertion_prefix + str + options.assertion_suffix; + }; + + var totalTests = 0; + var totalSuccess = 0; + var totalFailed = 0; + var totalErrors = 0; + + for (var suiteName in suites) { + if (suites.hasOwnProperty(suiteName)) { + var suite = suites[suiteName]; + var output = "\n" + suite.getName(); + if (suite.getDescription()) { + output += " - " + suite.getDescription(); + } + console.log(bold(output)); + var topics = suite.getTopics(); + for (var topicName in topics) { + if (topics.hasOwnProperty(topicName)) { + var topic = topics[topicName]; + output = topic.getName(); + if (topic.getDescription()) { + output += " - " + topic.getDescription(); + } + if (topic.getResult() === Result.PASS) { + console.log(ok(bold('✔ ' + output))); + } + if (topic.getResult() === Result.FAIL) { + console.log(error(bold('✖ ' + output))); + } + if (topic.getResult() === Result.ERROR) { + console.log(error(bold('\u274e ' + output))); + } + if (topic.getResult() === Result.NO_TESTS) { + console.log(error(bold('? ' + output + " (NO TESTS FOUND)"))); + } + + var testResults = topic.getTestResults(); + for (var i = 0; i < testResults.length; i += 1) { + var testResult = testResults[i]; + if (testResult.getResult() === Result.PASS) { + console.log(' ✔ ' + testResult.getName()); + } else if (testResult.getResult() === Result.FAIL) { + failTheme.stackFilters = [testResult.getTestFile()]; + var failError = formatErrors.highlightError(testResult.getError(), failTheme); + console.log(' ✖ ' + testResult.getName()); + console.log(failError.stack); +// console.log(ae.stack + '\n'); + } else if (testResult.getResult() === Result.ERROR) { + var formattedError = formatErrors.highlightError(testResult.getError(), errorTheme); + console.log(" \u274e " + testResult.getName() + '\n'); + console.log(formattedError.stack); + } else { + exceptions.ILLEGAL_STATE.thro("Unknown test result: " + testResult); + } + var detail = testResults[i].getName() + ": " + testResults[i].getResult(); + if (testResults[i].getError() !== null) { + detail += " - " + testResults[i].getError(); + } + } + } + } + + totalTests += suite.getTestCount(); + totalSuccess += suite.getSuccessCount(); + totalFailed += suite.getFailedCount(); + totalErrors += suite.getErrorCount(); + } + } + + + var summary = "\nTests: " + totalTests + ", Passed: " + totalSuccess + + ", Failed: " + totalFailed + ", Errors: " + totalErrors; + + if (totalFailed === 0 && totalErrors === 0) { + console.log(bold(ok(summary))); + } else { + console.log(bold(error(summary))); + } +} \ No newline at end of file diff --git a/lib/spiderTest.js b/lib/spiderTest.js index e0d8df8..2bea6e5 100644 --- a/lib/spiderTest.js +++ b/lib/spiderTest.js @@ -1,11 +1,12 @@ var fs = require("fs"); var spider = require("./spider"); var spiderOptions = require("./spiderOptions"); +var suiteManager = require("./suiteManager"); var timers = require("timers"); var urlModule = require("url"); var util = require("util"); -var SPIDER_COMPLETED_TIMEOUT = 1500; // todo make configurable +var SPIDER_COMPLETED_TIMEOUT = 1000; // todo make configurable var timeout; var autoSpiderAll = spider({ @@ -16,14 +17,20 @@ var autoSpiderAll = spider({ /** * Run spider tests. - * @param startUrl - * @param testsDir + * + * @param startUrl the initial url from which to start spidering + * @param testsDir directory containing the test definitions - this should be absolute unless baseDir + * is also specifed + * @param {Function} callback optional callback function invoked when all tests are completed * @param {String} baseDir optional working dir to change to if specified. If testsDir is relative then it is * relative to this dir. */ -exports.runTests = function (startUrl, testsDir, baseDir) { +exports.runTests = function (startUrl, testsDir, callback, baseDir) { + + var origDir; if(baseDir) { + origDir = process.cwd(); process.chdir(baseDir); } startUrl = resolveUrl(startUrl); @@ -32,8 +39,13 @@ exports.runTests = function (startUrl, testsDir, baseDir) { // todo handle case where there are no tests var done = function() { - if(base) - console.log("DONE!!!"); + if(origDir) { + process.chdir(origDir); + } + suiteManager.generateReport("console"); + if(callback) { + callback(); + } }; // :-( cannot think of another to know when all the URLs have been spidered except to timeout @@ -67,12 +79,35 @@ var executeMatchingTests = function (payload, $, testFiles) { var tests = require(testFile).tests; for (var topic in tests) { if (tests.hasOwnProperty(topic)) { - console.log(testFile + ": " + topic); +// console.log(testFile + ": " + topic); + var topicName = topic; + topic = tests[topic]; + var requestHref = payload.spider.currentUrl; + if(requestHref.match(topic.urlPattern)) { +// console.log("matched " + requestHref + " with " + topic.urlPattern); + var topicTests = topic.tests; + for(var topicTest in topicTests) { + if(topicTests.hasOwnProperty(topicTest)) { + var testName = topicTest; + topicTest = topicTests[topicTest]; + var testDetails = { + testName: testName, + topicName: topicName, + suiteName: requestHref, + test: topicTest, + testFile: testFile, + spiderPayload: payload, + $: $ + }; + suiteManager.runSuiteTest(testDetails); + } + } + } } } }); - console.log(util.inspect(payload)); +// console.log(util.inspect(payload)); }; diff --git a/lib/suiteManager.js b/lib/suiteManager.js index 9659a91..7c26475 100644 --- a/lib/suiteManager.js +++ b/lib/suiteManager.js @@ -92,8 +92,8 @@ var createSuite = function (name, description) { function topicCountTotal(countFunction) { var count = 0; - for(var topic in topics) { - if(topics.hasOwnProperty(topic)) { + for (var topic in topics) { + if (topics.hasOwnProperty(topic)) { count += topics[topic][countFunction](); } } @@ -144,7 +144,7 @@ var createSuite = function (name, description) { var TestResult = function () { }; -var createTestResult = function (name, result, error) { +var createTestResult = function (name, result, error, testFile) { if (result === Result.PASS && error) { exceptions.ILLEGAL_ARGUMENT.thro("There should be no error when the test result is a PASS"); @@ -164,6 +164,10 @@ var createTestResult = function (name, result, error) { return error; }; + o.getTestFile = function () { + return testFile; + }; + return o; }; @@ -173,155 +177,41 @@ var createNewSuite = function(name, description) { return suite; }; -var runSuiteTest = function (testName, topicName, suiteName, test) { +var runSuiteTest = function (testDetails) { var success = false; - var suite = suites[suiteName]; + var suite = suites[testDetails.suiteName]; if (!suite) { - suite = createNewSuite(suiteName); + suite = createNewSuite(testDetails.suiteName); } - var topic = suite.getTopic(topicName); + var topic = suite.getTopic(testDetails.topicName); if (!topic) { - topic = createTopic(topicName); + topic = createTopic(testDetails.topicName); suite.addTopic(topic); } try { - success = test(); - topic.addTestResult(createTestResult(testName, Result.PASS)); + success = testDetails.test(testDetails.spiderPayload, testDetails.$); + topic.addTestResult(createTestResult(testDetails.testName, Result.PASS, null, testDetails.testFile)); } catch (error) { + console.log("Caught error: " + error.name); if (error.name === "AssertionError") { - topic.addTestResult(createTestResult(testName, Result.FAIL, error)); + topic.addTestResult(createTestResult(testDetails.testName, Result.FAIL, error, testDetails.testFile)); } else { - topic.addTestResult(createTestResult(testName, Result.ERROR, error)); + topic.addTestResult(createTestResult(testDetails.testName, Result.ERROR, error, testDetails.testFile)); } } }; -exports.Suite = Suite; -exports.Result = Result; -exports.createTestResult = createTestResult; -exports.createSuite = createSuite; exports.runSuiteTest = runSuiteTest; - exports.generateReport = function (reporter) { - switch (reporter) { - case "junit": - generateJunitReport(); - break; - case "console": - generateDefaultReport(); - break; - default: - generateDefaultReport(); + reporter = reporter || "console"; + if (reporter.indexOf("/") === -1) { + reporter = "./reporters/" + reporter; } - - return totalFailed; + reporter = require(reporter); + reporter.report(suites, Result); }; -var options = { - "error_prefix": "\u001B[31m", - "error_suffix": "\u001B[39m", - "ok_prefix": "\u001B[32m", - "ok_suffix": "\u001B[39m", - "bold_prefix": "\u001B[1m", - "bold_suffix": "\u001B[22m", - "assertion_prefix": "\u001B[35m", - "assertion_suffix": "\u001B[39m" -}; - -function generateDefaultReport() { - - var error = function (str) { - return options.error_prefix + str + options.error_suffix; - }; - var ok = function (str) { - return options.ok_prefix + str + options.ok_suffix; - }; - var bold = function (str) { - return options.bold_prefix + str + options.bold_suffix; - }; - var assertion_message = function (str) { - return options.assertion_prefix + str + options.assertion_suffix; - }; - - var totalTests = 0; - var totalSuccess = 0; - var totalFailed = 0; - var totalErrors = 0; - - for (var suiteName in suites) { - if (suites.hasOwnProperty(suiteName)) { - var suite = suites[suiteName]; - var output = suite.getName(); - if (suite.getDescription()) { - output += " - " + suite.getDescription(); - } - console.log(bold(output)); - var topics = suite.getTopics(); - for (var topicName in topics) { - if (topics.hasOwnProperty(topicName)) { - var topic = topics[topicName]; - output = suite.getName(); - if (suite.getDescription()) { - output += " - " + suite.getDescription(); - } - if (topic.getResult() === Result.PASS) { - console.log(ok(bold('✔ ' + output))); - } - if (topic.getResult() === Result.FAIL) { - console.log(ok(bold('✖ ' + output))); - } - if (topic.getResult() === Result.ERROR) { - console.log(ok(bold('\u274e ' + output))); - } - if (topic.getResult() === Result.NO_TESTS) { - console.log(ok(bold('? ' + output + " (NO TESTS FOUND)"))); - } - - var testResults = topic.getTestResults(); - for (var i = 0; i < testResults.length; i += 1) { - var testResult = testResults[i]; - if (testResult.getResult() === Result.PASS) { - console.log(ok(' ✔ ' + testResult.getName())); - } - if (testResult.getResult() === Result.FAIL) { - var ae = formatAssertionError(testResult.getError()); - console.log(error(' ✖ ' + testResult.getName() + ": " + assertion_message(ae.message))); -// console.log(ae.stack + '\n'); - } - if (testResult.getResult() === Result.ERROR) { - console.log(error(" \u274e " + testResult.getName()) + '\n'); - console.log(testResult.getError()); - } - var detail = testResults[i].getName() + ": " + testResults[i].getResult(); - if (testResults[i].getError() !== null) { - detail += " - " + testResults[i].getError(); - } - } - } - } - - totalTests += suite.getTestCount(); - totalSuccess += suite.getSuccessCount(); - totalFailed += suite.getFailedCount(); - totalErrors += suite.getErrorCount(); - } - } - - - var summary = "Tests: " + totalTests + ", Passed: " + totalSuccess + - ", Failed: " + totalFailed + ", Errors: " + totalErrors; - - if (totalFailed === 0 && totalErrors === 0) { - console.log(bold(ok(summary))); - } else { - console.log(bold(error(summary))); - } -} - -function generateJunitReport() { - -} function formatAssertionError(e) { diff --git a/package.json b/package.json index 9289c60..3b0916a 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "dependencies" : { "exceptions" : "0.1.1", + "formaterrors" : "0.1.1", "request" : ">= 2.2.9", "routes" : ">= 0.1.0", "cheerio": ">= 0.3.2" diff --git a/test/testSpiderTest.js b/test/testSpiderTest.js index 86d9d2c..cbc926c 100644 --- a/test/testSpiderTest.js +++ b/test/testSpiderTest.js @@ -5,17 +5,6 @@ var should = require("should"); var spiderTestModule = loadModule("./lib/spiderTest.js"); var spiderTest = require("../lib/spiderTest"); -var server = express.createServer(); - -server.configure(function() { -// server.use(express.logger()); - server.use(server.router); - server.use(express["static"]("test/resources")); -}); - -server.listen(); -serverPort = server.address().port; - exports.testResolveUrl = function (test) { var url = spiderTestModule.resolveUrl("http://nodejs.org"); "http:".should.equal(url.protocol); @@ -24,7 +13,7 @@ exports.testResolveUrl = function (test) { "/".should.equal(url.path); "nodejs.org:80".should.equal(url.host); "http://nodejs.org:80/".should.equal(url.href); - + url = spiderTestModule.resolveUrl("hello"); "http:".should.equal(url.protocol); @@ -39,7 +28,7 @@ exports.testResolveUrl = function (test) { "80".should.equal(url.port); "localhost:80".should.equal(url.host); "http://localhost:80/".should.equal(url.href); - + test.done(); }; @@ -54,8 +43,22 @@ exports.testPopulateTestFiles = function (test) { }; exports.testRunTests = function (test) { + var server = express.createServer(); + + server.configure(function() { +// server.use(express.logger()); + server.use(server.router); + server.use(express["static"]("test/resources")); + }); + + server.listen(); + var serverPort = server.address().port; + // For some reason "process" is not available inside modules that are tested by nodeunit // this is why process is used here to establish the test directory - spiderTest.runTests("http://localhost:" + serverPort + "/testIndex.html", process.cwd() + "/" + "examples/tests"); - test.done(); + spiderTest.runTests("http://localhost:" + serverPort + "/testIndex.html", + process.cwd() + "/" + "examples/tests", function() { + server.close(); + test.done(); + }); }; \ No newline at end of file diff --git a/test/testSuiteManager.js b/test/testSuiteManager.js index 957e946..8dd14fa 100644 --- a/test/testSuiteManager.js +++ b/test/testSuiteManager.js @@ -4,11 +4,11 @@ var should = require("should"); var suiteManagerModule = loadModule("./lib/suiteManager.js"); var suiteManager = suiteManagerModule.module.exports; -var Suite = suiteManager.Suite; -var Result = suiteManager.Result; +var Suite = suiteManagerModule.Suite; +var Result = suiteManagerModule.Result; exports.testCreateTestResult = function (test) { - var testResult = suiteManager.createTestResult("testName", Result.PASS); + var testResult = suiteManagerModule.createTestResult("testName", Result.PASS); should.exist(testResult); should.equal("testName", testResult.getName()); should.equal(Result.PASS, testResult.getResult()); @@ -16,11 +16,11 @@ exports.testCreateTestResult = function (test) { }; exports.testCreateSuite = function (test) { - var suite = suiteManager.createSuite("suitename"); + var suite = suiteManagerModule.createSuite("suitename"); should.exist(suite); should.ok(suite instanceof Suite); should.equal("suitename", suite.getName()); - suite = suiteManager.createSuite("suitename", "description"); + suite = suiteManagerModule.createSuite("suitename", "description"); should.exist(suite); should.equal("suitename", suite.getName()); should.equal("description", suite.getDescription()); @@ -36,7 +36,13 @@ exports.testRunSuiteTest = function (test) { var test1 = function () { }; - suiteManager.runSuiteTest("Test1", "Topic1", "Suite1", test1); + var testDetails = { + testName: "Test1", + topicName: "Topic1", + suiteName: "Suite1", + test: test1 + }; + suiteManager.runSuiteTest(testDetails); var suites = suiteManagerModule.suites; should.exist(suites.Suite1); @@ -46,14 +52,14 @@ exports.testRunSuiteTest = function (test) { results.length.should.equal(1); results[0].getName().should.equal("Test1"); - results[0].getResult().should.equal(suiteManager.Result.PASS); + results[0].getResult().should.equal(suiteManagerModule.Result.PASS); topic.getName().should.equal("Topic1"); topic.getTestCount().should.equal(1); topic.getSuccessCount().should.equal(1); topic.getFailedCount().should.equal(0); topic.getErrorCount().should.equal(0); - topic.getResult().should.equal(suiteManager.Result.PASS); + topic.getResult().should.equal(suiteManagerModule.Result.PASS); suites.Suite1.getName().should.equal("Suite1"); suites.Suite1.getTestCount().should.equal(1);