<NME> test_util.js
<BEF> #!/usr/bin/env node

var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var q = require('q');

var cucumberConf = require(path.join(__dirname, '..', 'package.json')).cucumberConf;

var CommandlineTest = function(command) {
  var self = this;
  this.command_ = command;
  this.expectedExitCode_ = 0;
  this.stdioOnlyOnFailures_ = true;

let cucumberConf = require(path.join(__dirname, '..', 'package.json'))
  .cucumberConf;

let CommandlineTest = function(cucumberVersion, args) {
  let self = this;
  this.args_ = Array.isArray(args) ? args : args.split(/\s/);
  this.before_ = false;
  this.after_ = false;
  this.expectedExitCode_ = 0;
  this.verbose_ = false;
  this.debug_ = false;
  this.expectedErrors_ = [];
  this.expectedOutput_ = [];
  this.expectedEvents_ = [];

  switch (cucumberVersion) {
    case 1:
      this.cucumberVersion_ = cucumberConf.version1;
      break;

    case 2:
      this.cucumberVersion_ = cucumberConf.version2;
      break;

    case 3:
      this.cucumberVersion_ = cucumberConf.version3;
      break;

    case 4:
      this.cucumberVersion_ = cucumberConf.version4;
      break;

    case 5:
      this.cucumberVersion_ = cucumberConf.version5;
      break;

    case 6:
      this.cucumberVersion_ = cucumberConf.version6;
      break;

    case 7:
      this.cucumberVersion_ = cucumberConf.version7;
      break;

    default:
      throw new Error(`Cucumber ${cucumberVersion} not supported`);
  }

  this.cucumberVersion2 = function() {
    self.cucumberVersion_ = cucumberConf.version2;
    return self;
  };

  this.cucumberVersion3 = function() {
    self.cucumberVersion_ = cucumberConf.version3;
    return self;
  };

  this.cucumberVersion4 = function() {
  this.run = function() {
    process.env.MULTIDEP_CUCUMBER_VERSION = self.cucumberVesion_;

    var start = new Date().getTime();
    var testOutputPath = 'test_output_' + start + '.tmp';
    var output = '';

    var flushAndFail = function(errorMsg) {
      process.stdout.write(output);
      throw new Error(errorMsg);
    };
    return self;
  };
      if (!self.assertExitCodeOnly_) {
        self.command_ = self.command_ + ' --resultJsonOutputFile ' + testOutputPath;
      }
      var args = self.command_.split(/\s/);

      var test_process;

      if (self.stdioOnlyOnFailures_) {
        test_process = child_process.spawn(args[0], args.slice(1));
    if (expectedOutput) this.expectOutput(expectedOutput);
    return self;
  };

  this.expectExitCode = function(exitCode) {
    self.expectedExitCode_ = exitCode;
    return self;
  };

  this.before = function(callback) {
    self.before_ = callback;
    return self;
  };

  this.after = function(callback) {
    self.after_ = callback;
    return self;
  };

  this.verbose = function() {
    self.verbose_ = true;
    return self;
  };

  this.debug = function() {
    self.debug_ = true;
    return self;
  };

        return;
      }

      if (fs.existsSync(testOutputPath)) {
        var raw_data = fs.readFileSync(testOutputPath);
        var testOutput = JSON.parse(raw_data);
      } else {
        var testOutput = []
      }

      var actualErrors = [];
      var duration = 0;
      testOutput.forEach(function(specResult) {
        duration += specResult.duration;
        specResult.assertions.forEach(function(assertion) {
  this.expectOutput = function(output) {
    self.expectedOutput_.push(output);
    return self;
  };
      });

      self.expectedErrors_.forEach(function(expectedError) {
        var found = false;
        for (var i = 0; i < actualErrors.length; ++i) {
          var actualError = actualErrors[i];

          // if expected message is defined and messages don't match
          if (expectedError.message) {
            if (!actualError.errorMsg ||
                !actualError.errorMsg.match(new RegExp(expectedError.message))) {
                  continue;
                }
          }
          // if expected stackTrace is defined and stackTraces don't match
          if (expectedError.stackTrace) {
            if (!actualError.stackTrace ||
                !actualError.stackTrace.match(new RegExp(expectedError.stackTrace))) {
                  continue;
                }
          }
          found = true;
          break;
      }
    }

    return new Promise((resolve, reject) => {
        if (self.before_) self.before_();

        const cmd = 'node';
        const args = [
          require.resolve('protractor/bin/protractor'),
          '--disableChecks',
          '--resultJsonOutputFile',
          testOutputPath
        ].concat(self.args_);

        const env = {
          ...process.env,
          NODE_PATH: path.resolve(
              __dirname,
              `multidep_modules/${ self.cucumberVersion_.module }-${ self.cucumberVersion_.version }/node_modules`
          ),
        if (output.indexOf(out) < 0) {
          flushAndFail('expecting output `' + out + '`' + ' in `' + output + '`');
        }
      })

      if (actualErrors.length > 0) {
        flushAndFail('failed with ' + actualErrors.length + ' unexpected failures');
      }

      if (self.expectedMinTestDuration_
          && duration < self.expectedMinTestDuration_) {
            flushAndFail('expecting test min duration: ' +
                self.expectedMinTestDuration_ + ', actual: ' + duration);
      }
      if (self.expectedMaxTestDuration_
          && duration > self.expectedMaxTestDuration_) {
            flushAndFail('expecting test max duration: ' +
                self.expectedMaxTestDuration_ + ', actual: ' + duration);
      }
    }).fin(function() {
      try {
        test_process.on('exit', exitCode => resolve(exitCode));
      })
      .then(function(exitCode) {
        if (self.debug_) {
          console.log(output); // eslint-disable-line
        }

        if (self.expectedExitCode_ !== exitCode) {
          flushAndFail(
            `expecting exit code: ${
              self.expectedExitCode_
            }, actual: ${exitCode}`
          );
 *   For now, this means protractor tests (jasmine/mocha/cucumber).
 */
exports.Executor = function() {
  var tests = [];
  this.addCommandlineTest = function(command) {
    var test = new CommandlineTest(command);
    tests.push(test);
    return test;
  };

  this.execute = function() {
    var failed = false;

    (function runTests(i) {
      if (i < tests.length) {
        console.log('running: ' + tests[i].command_);
        tests[i].run().then(function() {
          console.log('>>> \033[1;32mpass\033[0m');
        }, function(err) {
          failed = true;
          console.log('>>> \033[1;31mfail: ' + err.toString() + '\033[0m');
        }).fin(function() {
          runTests(i + 1);
        }).done();
        self.expectedErrors_.forEach(function(expectedError) {
          let found = false;

          for (var i = 0; i < actualErrors.length; ++i) {
            let actualError = actualErrors[i];

            // if expected message is defined and messages don't match
            if (expectedError.message) {
              if (
                !actualError.errorMsg ||
                !actualError.errorMsg.match(new RegExp(expectedError.message))
              ) {
                continue;
              }
            }

            // if expected stackTrace is defined and stackTraces don't match
            if (expectedError.stackTrace) {
              if (
                !actualError.stackTrace ||
                !actualError.stackTrace.match(
                  new RegExp(expectedError.stackTrace)
                )
              ) {
                continue;
              }
            }

            found = true;
            break;
          }

          if (!found) {
            if (expectedError.message && expectedError.stackTrace) {
              flushAndFail(
                'did not fail with expected error with message: [' +
                  expectedError.message +
                  '] and stackTrace: [' +
                  expectedError.stackTrace +
                  ']'
              );
            } else if (expectedError.message) {
              flushAndFail(
                'did not fail with expected error with message: [' +
                  expectedError.message +
                  ']'
              );
            } else if (expectedError.stackTrace) {
              flushAndFail(
                'did not fail with expected error with stackTrace: [' +
                  expectedError.stackTrace +
                  ']'
              );
            }
          } else {
            actualErrors.splice(i, 1);
          }
        });

        self.expectedOutput_.forEach(function(out) {
          if (output.indexOf(out) < 0) {
            flushAndFail(
              'expecting output `' + out + '`' + ' in `' + output + '`'
            );
          }
        });

        if (actualErrors.length > 0) {
          flushAndFail(
            'failed with ' + actualErrors.length + ' unexpected failures'
          );
        }

        if (self.after_) self.after_();
        if (self.then_) self.then_();
      })
      .then(removeTestOutput, removeTestOutput);
  };
};

function runOne(cucumberVersion, args) {
  return new CommandlineTest(cucumberVersion, args);
}

module.exports = {
  runOne: runOne.bind(null, 1),
  testCucumber1: runOne.bind(null, 1),
  testCucumber2: runOne.bind(null, 2),
  testCucumber3: runOne.bind(null, 3),
  testCucumber4: runOne.bind(null, 4),
  testCucumber5: runOne.bind(null, 5),
  testCucumber6: runOne.bind(null, 6)
};

<MSG> [#72] add linting

<DFF> @@ -1,14 +1,16 @@
 #!/usr/bin/env node
 
-var child_process = require('child_process');
-var fs = require('fs');
-var path = require('path');
-var q = require('q');
+/* eslint no-console: 0 */
 
-var cucumberConf = require(path.join(__dirname, '..', 'package.json')).cucumberConf;
+let child_process = require('child_process');
+let fs = require('fs');
+let path = require('path');
+let q = require('q');
 
-var CommandlineTest = function(command) {
-  var self = this;
+let cucumberConf = require(path.join(__dirname, '..', 'package.json')).cucumberConf;
+
+let CommandlineTest = function(command) {
+  let self = this;
   this.command_ = command;
   this.expectedExitCode_ = 0;
   this.stdioOnlyOnFailures_ = true;
@@ -75,11 +77,11 @@ var CommandlineTest = function(command) {
   this.run = function() {
     process.env.MULTIDEP_CUCUMBER_VERSION = self.cucumberVesion_;
 
-    var start = new Date().getTime();
-    var testOutputPath = 'test_output_' + start + '.tmp';
-    var output = '';
+    let start = new Date().getTime();
+    let testOutputPath = 'test_output_' + start + '.tmp';
+    let output = '';
 
-    var flushAndFail = function(errorMsg) {
+    let flushAndFail = function(errorMsg) {
       process.stdout.write(output);
       throw new Error(errorMsg);
     };
@@ -88,9 +90,9 @@ var CommandlineTest = function(command) {
       if (!self.assertExitCodeOnly_) {
         self.command_ = self.command_ + ' --resultJsonOutputFile ' + testOutputPath;
       }
-      var args = self.command_.split(/\s/);
+      let args = self.command_.split(/\s/);
 
-      var test_process;
+      let test_process;
 
       if (self.stdioOnlyOnFailures_) {
         test_process = child_process.spawn(args[0], args.slice(1));
@@ -126,15 +128,17 @@ var CommandlineTest = function(command) {
         return;
       }
 
+      let testOutput;
+
       if (fs.existsSync(testOutputPath)) {
-        var raw_data = fs.readFileSync(testOutputPath);
-        var testOutput = JSON.parse(raw_data);
+        let raw_data = fs.readFileSync(testOutputPath);
+        testOutput = JSON.parse(raw_data);
       } else {
-        var testOutput = []
+        testOutput = [];
       }
 
-      var actualErrors = [];
-      var duration = 0;
+      let actualErrors = [];
+      let duration = 0;
       testOutput.forEach(function(specResult) {
         duration += specResult.duration;
         specResult.assertions.forEach(function(assertion) {
@@ -145,23 +149,21 @@ var CommandlineTest = function(command) {
       });
 
       self.expectedErrors_.forEach(function(expectedError) {
-        var found = false;
+        let found = false;
         for (var i = 0; i < actualErrors.length; ++i) {
-          var actualError = actualErrors[i];
+          let actualError = actualErrors[i];
 
           // if expected message is defined and messages don't match
           if (expectedError.message) {
-            if (!actualError.errorMsg ||
-                !actualError.errorMsg.match(new RegExp(expectedError.message))) {
-                  continue;
-                }
+            if (!actualError.errorMsg || !actualError.errorMsg.match(new RegExp(expectedError.message))) {
+              continue;
+            }
           }
           // if expected stackTrace is defined and stackTraces don't match
           if (expectedError.stackTrace) {
-            if (!actualError.stackTrace ||
-                !actualError.stackTrace.match(new RegExp(expectedError.stackTrace))) {
-                  continue;
-                }
+            if (!actualError.stackTrace || !actualError.stackTrace.match(new RegExp(expectedError.stackTrace))) {
+              continue;
+            }
           }
           found = true;
           break;
@@ -188,21 +190,17 @@ var CommandlineTest = function(command) {
         if (output.indexOf(out) < 0) {
           flushAndFail('expecting output `' + out + '`' + ' in `' + output + '`');
         }
-      })
+      });
 
       if (actualErrors.length > 0) {
         flushAndFail('failed with ' + actualErrors.length + ' unexpected failures');
       }
 
-      if (self.expectedMinTestDuration_
-          && duration < self.expectedMinTestDuration_) {
-            flushAndFail('expecting test min duration: ' +
-                self.expectedMinTestDuration_ + ', actual: ' + duration);
+      if (self.expectedMinTestDuration_ && duration < self.expectedMinTestDuration_) {
+        flushAndFail('expecting test min duration: ' + self.expectedMinTestDuration_ + ', actual: ' + duration);
       }
-      if (self.expectedMaxTestDuration_
-          && duration > self.expectedMaxTestDuration_) {
-            flushAndFail('expecting test max duration: ' +
-                self.expectedMaxTestDuration_ + ', actual: ' + duration);
+      if (self.expectedMaxTestDuration_ && duration > self.expectedMaxTestDuration_) {
+        flushAndFail('expecting test max duration: ' + self.expectedMaxTestDuration_ + ', actual: ' + duration);
       }
     }).fin(function() {
       try {
@@ -222,24 +220,24 @@ var CommandlineTest = function(command) {
  *   For now, this means protractor tests (jasmine/mocha/cucumber).
  */
 exports.Executor = function() {
-  var tests = [];
+  let tests = [];
   this.addCommandlineTest = function(command) {
-    var test = new CommandlineTest(command);
+    let test = new CommandlineTest(command);
     tests.push(test);
     return test;
   };
 
   this.execute = function() {
-    var failed = false;
+    let failed = false;
 
     (function runTests(i) {
       if (i < tests.length) {
         console.log('running: ' + tests[i].command_);
         tests[i].run().then(function() {
-          console.log('>>> \033[1;32mpass\033[0m');
+          console.log('>>> \x1B[1;32mpass\x1B[0m');
         }, function(err) {
           failed = true;
-          console.log('>>> \033[1;31mfail: ' + err.toString() + '\033[0m');
+          console.log('>>> \x1B[1;31mfail: ' + err.toString() + '\x1B[0m');
         }).fin(function() {
           runTests(i + 1);
         }).done();

[#72] add linting