Repository: nightwatchjs/nightwatch
Pull request #3528 (2022-12-21 13:56:42)

Features/add reporter capabilities for plugins
## Changes
- added a method in testResults to append data in current test section
- added capabilities to consider global reporter from plugins
- added test


Patch:
diff --git a/lib/api/_loaders/plugin.js b/lib/api/_loaders/plugin.js
index 7caffb7b..bbbac394 100644
--- a/lib/api/_loaders/plugin.js
+++ b/lib/api/_loaders/plugin.js
@@ -2,9 +2,22 @@ const fs = require('fs');
 const path = require('path');
 const Utils = require('../../utils');
 
+const __plugins = {};
+
 module.exports = class PluginLoader {
+
+  static get plugins() {
+    return __plugins;
+  }
+
+
   static load(pluginName) {
     try {
+
+      if (PluginLoader.plugins[pluginName]) {
+        return PluginLoader.plugins[pluginName];
+      }
+
       const plugin = {};
       const pluginPath = Utils.getPluginPath(pluginName);
       const dirName = path.dirname(pluginPath);
@@ -50,7 +63,9 @@ module.exports = class PluginLoader {
         plugin.transforms = fs.existsSync(transformsPath) ? Utils.requireModule(transformsPath) : null;
       }
 
-      return plugin;
+      PluginLoader.plugins[pluginName] = plugin;
+
+      return PluginLoader.plugins[pluginName];
     } catch (err) {
       const msg = err.message.split('\n')[0];
       const error = new Error(`Unable to load plugin: ${pluginName}: [${err.code}] ${msg}`);
diff --git a/lib/reporter/global-reporter.js b/lib/reporter/global-reporter.js
index 3dd974fe..ddefa8a5 100644
--- a/lib/reporter/global-reporter.js
+++ b/lib/reporter/global-reporter.js
@@ -9,6 +9,7 @@ const {Logger, beautifyStackTrace} = Utils;
 const Results = require('./results.js');
 const AxeReport = require('./axe-report.js');
 const Summary = require('./summary.js');
+const PluginLoader = require('../api/_loaders/plugin');
 
 const colors = Logger.colors;
 
@@ -325,25 +326,50 @@ module.exports = class GlobalReporter {
     return message;
   }
 
+  getPluginReporters() {
+    const plugins = this.settings.plugins || [];
+
+    const reporters = plugins.reduce((prev, pluginName) => {
+      const plugin = PluginLoader.load(pluginName);
+      if (plugin.globals && plugin.globals.reporter && (typeof plugin.globals.reporter === 'function')) {
+        prev.push(plugin.globals.reporter);
+      }
+
+      return prev;
+    }, []);
+
+    return reporters;
+  }
+
   runCustomGlobalReporter(globalResults) {
-    const customReporter = this.settings.globals.reporter;
+    const pluginReporters = this.getPluginReporters();
+    let customReporters = this.settings.globals.reporter;
+
+    if (customReporters && !Array.isArray(customReporters))  {
+      customReporters = [customReporters];
+    }
 
-    return new Promise((resolve, reject) => {
-      const callbackTimeoutId = setTimeout(() => {
-        reject(new Error('Timeout while waiting (20s) for the custom global reporter callback to be called.'));
-      }, this.settings.globals.customReporterCallbackTimeout);
+    customReporters = customReporters.concat(pluginReporters);
 
-      try {
-        const reporterFnAsync = Utils.makeFnAsync(2, customReporter, this.settings.globals);
-        reporterFnAsync.call(this.settings.globals, globalResults, function() {
+    const results = customReporters.map(customReporter => {
+      return new Promise((resolve, reject) => {
+        const callbackTimeoutId = setTimeout(()=>{
+          reject(new Error('Timeout while waiting (20s) for the custom global reporter callback to be called.'));
+        }, this.settings.globals.customReporterCallbackTimeout);
+        try {
+          const reporterFnAsync = Utils.makeFnAsync(2, customReporter, this.settings.globals);
+          reporterFnAsync.call(this.settings.globals, globalResults,  function() {
+            clearTimeout(callbackTimeoutId);
+            resolve();
+          });
+        } catch (err) {
           clearTimeout(callbackTimeoutId);
-          resolve();
-        });
-      } catch (err) {
-        clearTimeout(callbackTimeoutId);
-        reject(err);
-      }
+          reject(err);
+        }
+      });
     });
+
+    return Promise.all(results);
   }
 
   save() {
diff --git a/lib/reporter/results.js b/lib/reporter/results.js
index 6d085e8f..36d6f7c4 100644
--- a/lib/reporter/results.js
+++ b/lib/reporter/results.js
@@ -1,4 +1,5 @@
 const AssertionError = require('assertion-error');
+const {merge: lodashMerge} = require('lodash');
 const Utils = require('../utils');
 const {Logger} = Utils;
 
@@ -197,7 +198,7 @@ module.exports = class Results {
     };
 
     Object.keys(this.testcases).forEach(key => {
-      let testcase = this.testcases[key];
+      const testcase = this.testcases[key];
       if (testcase.lastError) {
         //lastError = testcase.lastError;
       }
@@ -436,11 +437,11 @@ module.exports = class Results {
   setCurrentTest(testcase) {
     this.currentTest = testcase;
 
-    let testName = testcase.testName;
+    const testName = testcase.testName;
     this.currentTestName = testName;
     this.testcases[testName] = this.createTestCaseResults(testcase);
 
-    let index = this.skipped.indexOf(testName);
+    const index = this.skipped.indexOf(testName);
     if (index > -1) {
       this.skipped.splice(index, 1);
       this.__skippedCount -= 1;
@@ -461,8 +462,8 @@ module.exports = class Results {
   }
 
   logScreenshotFile(screenshotFile) {
-    let assertions = this.currentTestResult.assertions;
-    let lastAssertion = assertions[assertions.length - 1];
+    const assertions = this.currentTestResult.assertions;
+    const lastAssertion = assertions[assertions.length - 1];
 
     if (lastAssertion) {
       lastAssertion.screenshots = lastAssertion.screenshots || [];
@@ -486,6 +487,16 @@ module.exports = class Results {
     return Results.TEST_SKIP;
   }
 
+
+  /**
+   * Appends data in current testSections data.
+   *  
+   * @param {Object} data 
+   */
+  appendTestResult(data) {
+    lodashMerge(this.testSections, data);
+  }
+
   /**
    * Combines all the individual test suite reports into a global one
    *
@@ -495,7 +506,7 @@ module.exports = class Results {
    */
   static createGlobalReport(suiteResultsArr, initialReport) {
     return suiteResultsArr.reduce((prev, item) => {
-      let results = item.results;
+      const results = item.results;
 
       if (results.lastError) {
         prev.lastError = results.lastError;


Test patch:
diff --git a/test/extra/plugin/nightwatch/globals.js b/test/extra/plugin/nightwatch/globals.js
new file mode 100644
index 00000000..690f1559
--- /dev/null
+++ b/test/extra/plugin/nightwatch/globals.js
@@ -0,0 +1,6 @@
+module.exports = {
+  reporter(results, done) {
+    this.reporterCount++;
+    done();
+  }
+};
\ No newline at end of file
diff --git a/test/src/runner/testRunWithGlobalReporter.js b/test/src/runner/testRunWithGlobalReporter.js
index 8a7b8f38..750467ba 100644
--- a/test/src/runner/testRunWithGlobalReporter.js
+++ b/test/src/runner/testRunWithGlobalReporter.js
@@ -99,4 +99,26 @@ describe('testRunWithGlobalReporter', function() {
       assert.strictEqual(reporterCount, 1);
     }).catch(err => (err))
   });
+
+
+  it('test plugin global reporter', function() {
+    const testsPath = path.join(__dirname, '../../sampletests/before-after');
+    const pluginPath = path.join(__dirname, '../../extra/plugin');
+    const globals = {
+      reporterCount: 0,
+      reporter(results, done) {
+        this.reporterCount++;
+        done();
+      }
+    };
+
+    return runTests(testsPath, settings({
+      plugins: [pluginPath],
+      globals,
+      output_folder: false
+    }))
+      .then(err => {
+        assert.strictEqual(globals.reporterCount, 2);
+      });
+  });
 });