UNPKG

nightwatch

Version:

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

296 lines (243 loc) 8.57 kB
const open = require('open'); const path = require('path'); const lodashPick = require('lodash/pick'); const HtmlReact = require('@nightwatch/html-reporter-template'); const AnsiConverter = require('ansi-to-html'); const Utils = require('../../utils'); const BaseReporter = require('../base-reporter.js'); const {Logger} = Utils; class HtmlReporter extends BaseReporter { static get hookNames(){ return ['__before_hook', '__after_hook', '__global_beforeEach_hook', '__global_afterEach_hook']; } static get ansiConverter(){ return new AnsiConverter(); } openReporter(fileName) { return open(fileName) .catch(err => { Logger.error('Error opening the report: ', err.message); }); } getFolderPrefix() { let folderPrefix = ''; const {folder_format} = this.options; if (folder_format) { if (typeof folder_format === 'function') { folderPrefix = folder_format(this.results); } else if (typeof folder_format === 'string') { folderPrefix = folder_format; } } return folderPrefix; } getFileName() { let fileName = 'index'; const {filename_format} = this.options; if (filename_format) { if (typeof filename_format === 'function') { fileName = filename_format(this.results); } else if (typeof filename_format === 'string') { fileName = filename_format; } } return fileName; } computePassedAndFailedCounts(module) { const result = {passedCount: 0, failedCount: 0}; if (!module || !module.completedSections) { return result; } for (const sectionName of Object.keys(module.completedSections)) { if (HtmlReporter.hookNames.includes(sectionName)) { continue; } const section = module.completedSections[sectionName]; if (section.status === 'pass') { result.passedCount += 1; } else { result.failedCount += 1; } } return result; } createInitialResult(module) { const {passedCount, failedCount} = this.computePassedAndFailedCounts(module); const sessionCapabilities = module.sessionCapabilities || {}; return { metadata: { platformName: sessionCapabilities.platformName, device: 'desktop', browserName: sessionCapabilities.browserName, browserVersion: sessionCapabilities.browserVersion, executionMode: 'local' }, stats: { passed: passedCount, failed: failedCount, skipped: module.skippedCount, total: passedCount + failedCount + module.skippedCount, time: module.timeMs }, modules: {} }; } aggregateEnvironments(testEnv, moduleKey, module) { if (!this.environments[testEnv]) { this.environments[testEnv] = this.createInitialResult(module); } else { const {passedCount, failedCount} = this.computePassedAndFailedCounts(module); const sessionCapabilities = module.sessionCapabilities || {}; this.environments[testEnv].stats.passed += passedCount; this.environments[testEnv].stats.failed += failedCount; this.environments[testEnv].stats.skipped += module.skippedCount; this.environments[testEnv].stats.total += passedCount + failedCount + module.skippedCount; this.environments[testEnv].stats.time += module.timeMs || this.environments[testEnv].stats.time; this.environments[testEnv].metadata.platformName = this.environments[testEnv].metadata.platformName || sessionCapabilities.platformName; this.environments[testEnv].metadata.browserName = this.environments[testEnv].metadata.browserName || sessionCapabilities.browserName; this.environments[testEnv].metadata.browserVersion = this.environments[testEnv].metadata.browserVersion || sessionCapabilities.browserVersion; } this.environments[testEnv].modules[moduleKey] = this.adaptModule(module); } aggregateStats() { const startTime = new Date(this.results.startTimestamp).getTime(); const endTime = new Date(this.results.endTimestamp).getTime(); const stats = { total: 0, passed: 0, failed: 0, skipped: 0, time: endTime - startTime }; for (const envName of Object.keys(this.environments)) { const env = this.environments[envName]; stats.passed += env.stats.passed; stats.failed += env.stats.failed; stats.skipped += env.stats.skipped; stats.total += env.stats.total; } return stats; } getGlobalMetadata() { return { date: new Date() }; } adaptModule(module) { // Pick only the necessary fields const result = lodashPick(module, [ 'completedSections', 'rawHttpOutput', 'assertionsCount', 'lastError', 'skipped', 'time', 'timeMs', 'errmessages', 'testsCount', 'skippedCount', 'failedCount', 'errorsCount', 'passedCount', 'group', 'modulePath', 'startTimestamp', 'endTimestamp', 'sessionCapabilities', 'sessionId', 'projectName', 'buildName', 'testEnv', 'isMobile', 'status', 'seleniumLog', 'tests', 'failures', 'errors' ]); // Convert to absolute path for (const sectionName of Object.keys(result.completedSections)) { const section = result.completedSections[sectionName]; for (const command of section.commands) { const {output_folder} = this.options; const destFolder = path.join(output_folder, this.getFolderPrefix(), 'nightwatch-html-report'); if (command.screenshot) { command.screenshot = path.relative(destFolder, command.screenshot); } if (command.domSnapshot && command.domSnapshot.snapshotFilePath) { command.domSnapshot.snapshotFilePath = path.resolve(process.cwd(), command.domSnapshot.snapshotFilePath); } } } // Add skipped tests by user in completed section module.skippedByUser && module.skippedByUser.forEach(skippedTestName => { module.completedSections[skippedTestName] = { status: 'skip', runtimeFailure: false }; }); // Add skipped tests at runtime in completed section module.skippedAtRuntime && module.skippedAtRuntime.forEach(skippedTestName => { module.completedSections[skippedTestName] = { status: 'skip', runtimeFailure: true }; }); return result; } adaptResults() { const {modulesWithEnv} = this.results; this.environments = {}; Object.keys(modulesWithEnv).forEach((env) => { const modules = modulesWithEnv[env]; // Make module paths absolute this.results.modulesWithEnv[env] = Object.keys(modules).reduce((prev, value) => { this.results.modulesWithEnv[env][value].modulePath = this.results.modulesWithEnv[env][value].modulePath.replace(process.cwd(), ''); prev[value] = this.results.modulesWithEnv[env][value]; return prev; }, {}); Object.keys(modules).forEach((moduleKey) => { const module = modules[moduleKey]; this.adaptAssertions(module); this.aggregateEnvironments(env, moduleKey, module); }); }); return { environments: this.environments, stats: this.aggregateStats(), metadata: this.getGlobalMetadata() }; } async writeReport() { const results = this.adaptResults(); const {output_folder, openReport: shouldOpenReport} = this.options; const destFolder = path.join(output_folder, this.getFolderPrefix(), 'nightwatch-html-report'); const fileName = `${this.getFileName()}.html`; const filePath = path.join(destFolder, `${this.getFileName()}.html`); const jsonString = JSON.stringify(results); HtmlReact.writeNightwatchHTMLReport(destFolder, fileName, jsonString); Logger.logDetailedMessage(Logger.colors.stack_trace(` Wrote HTML report file to: ${path.resolve(filePath)}` + '\n'), 'info'); if (shouldOpenReport) { return this.openReporter(filePath); } } } module.exports = (function() { return { write(results, options, callback) { const reporter = new HtmlReporter(results, options); reporter.writeReport() .then(_ => { callback(); }) .catch(err => { Logger.error(err); callback(err); }); }, adaptResults(results, options) { const reporter = new HtmlReporter(results, options); return reporter.adaptResults(); } }; })();