UNPKG

nightwatch

Version:

Easy to use Node.js based end-to-end testing solution for web applications using the W3C WebDriver API.

295 lines (241 loc) 8.5 kB
const Utils = require('../utils'); const TestResults = require('./results.js'); const SimplifiedReporter = require('./simplified.js'); const {Logger, Screenshots} = Utils; const {colors} = Logger; class Reporter extends SimplifiedReporter { static printAssertions(testcase) { testcase.assertions.forEach(function(a) { if (a.failure !== false) { let message = a.stackTrace.split('\n'); message.unshift(a.fullMsg); Utils.showStackTrace(message.join('\n')); } }); } /** * * @param {Array} tests * @param {SuiteRetries} suiteRetries * @param {Object} settings * @param {Object} addOpts */ constructor({settings, tests, suiteRetries, addOpts = {}}) { super(settings); this.suiteRetries = suiteRetries; this.suiteName = addOpts.suiteName; this.testResults = new TestResults(tests, addOpts); this.currentContext = null; this.reporter = addOpts.repoter; this.testResults.initCurrentTest({ module: addOpts.moduleKey, testName: '', group: addOpts.groupName }); }; /** * This is the exported property on the Nightwatch api object, which is passed on to tests * * @return {null|object} */ get currentTest() { return this.testResults.currentTest; } /** * @param {TestCase} testcase * @param {Context} context */ setCurrentTest(testcase, context) { this.currentContext = context; this.testResults.setCurrentTest(testcase); } resetCurrentTestName() { this.testResults.resetCurrentTestName(); } get unitTestsMode() { return this.currentContext ? this.currentContext.unitTestsMode : this.settings.unit_tests_mode; } get currentTestCasePassed() { return this.testResults.currentTestCasePassed; } get allTestsPassed() { return this.testResults.testsPassed(); } /** * @param {Error} err * * @return {boolean} */ shouldIncrementTotalCount(err) { const {currentTest} = this; const currentTestName = this.testResults.getCurrentTestName(); const shouldRetryTestcase = currentTest && this.suiteRetries && this.suiteRetries.shouldRetryTest(currentTestName); let incrementTotalCount = err.incrementErrorCount || Utils.isUndefined(err.incrementErrorCount); if (err.incrementErrorsNo || shouldRetryTestcase) { incrementTotalCount = false; } return incrementTotalCount; } setFileNamePrefix(prefix) { this.testResults.reportPrefix = prefix; } setElapsedTime() { this.testResults.setElapsedTime(); } testSuiteFinished() { this.testResults.setTotalElapsedTime(); } exportResults() { return this.testResults.export; } //////////////////////////////////////////////////////////// // Results logging //////////////////////////////////////////////////////////// logTestCase(testName) { if (this.settings.live_output || !this.settings.parallel_mode) { // if (this.settings.silent) { // const ora = require('ora'); // this.runSpinner = ora({ // text: `Running ${colors.green(testName)}${colors.stack_trace(':')}\n` // }).start(); // } else { // eslint-disable-next-line no-console console.log(`${(!this.settings.silent?'\n\n':'')}\n Running ${colors.green(testName)}${colors.stack_trace(':')}`); const {columns = 100} = process.stdout; // eslint-disable-next-line no-console console.log(colors.stack_trace(new Array(Math.max(100, Math.floor(columns/2))).join('─'))); //} } else { // eslint-disable-next-line no-console console.log(`Results for: ${colors.green(testName)}`); } } /** * @param {Object} result */ logAssertResult(result) { this.testResults.logAssertion(result); } registerPassed(message) { Logger.logDetailedMessage(` ${colors.green(Utils.symbols.ok)} ${message}`); this.testResults.incrementPassedCount(); } registerFailed(err) { const incrementTotal = this.shouldIncrementTotalCount(err); this.testResults .setLastError(err, {incrementTotal}) .incrementFailedCount(incrementTotal); } registerTestError(err) { if (err.registered) { return; } super.registerTestError(err); const incrementTotal = this.shouldIncrementTotalCount(err); this.testResults .setLastError(err, {incrementTotal, addToErrArray: true}) .incrementErrorCount(incrementTotal); } /** * Subtracts the number of passed assertions from the total assertions count */ resetCurrentTestPassedCount() { let assertionsCount = this.testResults.currentTestResult.passed; this.testResults.subtractPassedCount(assertionsCount); } printTestResult() { let ok = false; if (this.testResults.currentTestCasePassed) { ok = true; } let elapsedTime = this.testResults.currentTestElapsedTime; let currentTestResult = this.testResults.currentTestResult; const Concurrency = require('../runner/concurrency'); const isChildProcess = Concurrency.isChildProcess(); if (isChildProcess || !this.settings.detailed_output || this.unitTestsMode) { this.printSimplifiedTestResult(ok, elapsedTime, isChildProcess); return; } if (ok && currentTestResult.passed > 0) { Logger.logDetailedMessage(`\n${colors.green('OK.')} ${colors.green(currentTestResult.passed)} assertions passed. (${Utils.formatElapsedTime(elapsedTime, true)})`); } else if (ok && currentTestResult.passed === 0) { if (this.settings.start_session) { Logger.logDetailedMessage(colors.green('No assertions ran.\n'), 'warn'); } } else { let failureMsg = this.getFailureMessage(); Logger.logDetailedMessage(`\n${colors.red('FAILED:')} ${failureMsg} (${Utils.formatElapsedTime(elapsedTime, true)})`); } } /** * @param {boolean} ok * @param {number} elapsedTime * @param {boolean} isChildProcess */ printSimplifiedTestResult(ok, elapsedTime, isChildProcess) { const {currentTest} = this; let result = [colors[ok ? 'green': 'red'](Utils.symbols[ok ? 'ok' : 'fail'])]; if (!this.unitTestsMode) { if (isChildProcess) { result.push(colors.white(process.env.__NIGHTWATCH_ENV, colors.background.black)); } result.push(colors.cyan('[' + this.suiteName + ']')); } let testName = currentTest.name; result.push(ok ? testName : colors.red(testName)); if (elapsedTime > 20) { result.push(colors.yellow('(' + Utils.formatElapsedTime(elapsedTime, true) + ')')); } // eslint-disable-next-line no-console console.log(result.join(' ')); if (ok || !currentTest) { return; } const {results} = currentTest; if (this.unitTestsMode && results.lastError) { Logger.error(results.lastError); } else { Reporter.printAssertions(results); } } getFailureMessage() { let failureMsg = []; let currentTestResult = this.testResults.currentTestResult; if (currentTestResult.failed > 0){ failureMsg.push(`${colors.red(currentTestResult.failed)} assertions failed`); } if (currentTestResult.errors > 0) { failureMsg.push(`${colors.red(currentTestResult.errors)} errors`); } if (currentTestResult.passed > 0) { failureMsg.push(`${colors.green(currentTestResult.passed)} passed`); } if (currentTestResult.skipped > 0) { failureMsg.push(`${colors.blue(currentTestResult.skipped)} skipped`); } return failureMsg.join(', ').replace(/,([^,]*)$/g, function(p0, p1) { return ` and ${p1}`; }); } //////////////////////////////////////////////////////////// // Screenshots //////////////////////////////////////////////////////////// /** * @deprecated only used by JSONWire * @param result * @param screenshotContent */ saveErrorScreenshot(result, screenshotContent) { if (this.settings.screenshots.on_error && screenshotContent) { const {currentTest} = this; const prefix = `${currentTest.module}/${currentTest.name}`; const fileName = Screenshots.getFileName(prefix, true, this.settings.screenshots.path); // FIXME: make this async / handle callback Screenshots.writeScreenshotToFile(fileName, screenshotContent); this.testResults.logScreenshotFile(fileName); } } } module.exports = Reporter; module.exports.Simplified = require('./simplified.js'); module.exports.GlobalReporter = require('./global-reporter.js');