Repository: mochajs/mocha

Problem statement:
Regression caused by the deprecation of `enableTimeouts`
### Description

There is a regression caused by the deprecation of `enableTimeouts`

As you cas see here: https://github.com/mochajs/mocha/pull/3556/files#diff-aa849a970cef551664c12f04a4209f6fR121
`enableTimeouts` was marked as deprecated and the use of `timeout: false` should replace it.

But in the same time, we removed:
```js
if (typeof options.timeout !== 'undefined' && options.timeout !== null) {
    this.timeout(options.timeout);
```

The bug is present with `grunt-mocha-test`. As you can see, `grunt-mocha-test` parse all options as undefined:
```js
{ grep: undefined,
  reporter: 'spec',
  bail: false,
  ui: undefined,
  timeout: undefined,
  invert: undefined,
  ignoreLeaks: undefined,
  growl: undefined,
  globals: undefined,
  require: undefined,
  colors: undefined,
  slow: undefined 
}
```

The bug is not present with `grunt-simple-mocha`

Should we kept it while `enableTimeouts` is marked as deprecated ?

### Steps to Reproduce
```sh
npm install grunt grunt-mocha-test
```

in `Gruntfile.js`

```js
module.exports = function(grunt) {

  grunt.loadNpmTasks('grunt-mocha-test');

  grunt.initConfig({
    mochaTest: {
      test: {
        src: ['test/**/*.js']
      }
    }
  });

  grunt.registerTask('default', 'mochaTest');

};
```

Then launch test
```sh
grunt mochaTest
```

### Expected behavior

Mocha v6 should work with options like {timeout: undefined} because there is only a deprecation warning in v6 for `enableTimeouts` option.

### Actual behavior:

TypeError: Cannot read property 'toString' of undefined
  at Suite.timeout (/home/fwadmin-server/smc/node_modules/mocha/lib/suite.js:124:10)
  at Mocha.timeout (/home/fwadmin-server/smc/node_modules/mocha/lib/mocha.js:625:14)
  at new Mocha (/home/fwadmin-server/smc/node_modules/mocha/lib/mocha.js:124:8)
  at new MochaWrapper (/home/fwadmin-server/smc/node_modules/grunt-mocha-test/tasks/lib/MochaWrapper.js:30:15)

### Reproduces how often:
  Always

### Versions
  6.0.2


Hints:
Thanks.  Looks like I obliterated this guard.  

Patch:
diff --git a/lib/mocha.js b/lib/mocha.js
--- a/lib/mocha.js
+++ b/lib/mocha.js
@@ -120,12 +120,15 @@ function Mocha(options) {
     utils.deprecate(
       'enableTimeouts is DEPRECATED and will be removed from a future version of Mocha. Instead, use "timeout: false" to disable timeouts.'
     );
+    if (options.enableTimeouts === false) {
+      this.timeout(0);
+    }
+  }
+
+  // this guard exists because Suite#timeout does not consider `undefined` to be valid input
+  if (typeof options.timeout !== 'undefined') {
+    this.timeout(options.timeout === false ? 0 : options.timeout);
   }
-  this.timeout(
-    options.enableTimeouts === false || options.timeout === false
-      ? 0
-      : options.timeout
-  );
 
   if ('retries' in options) {
     this.retries(options.retries);
diff --git a/lib/suite.js b/lib/suite.js
--- a/lib/suite.js
+++ b/lib/suite.js
@@ -114,6 +114,7 @@ Suite.prototype.clone = function() {
  * Set or get timeout `ms` or short-hand such as "2s".
  *
  * @private
+ * @todo Do not attempt to set value if `ms` is undefined
  * @param {number|string} ms
  * @return {Suite|number} for chaining
  */


Test patch:
diff --git a/test/unit/mocha.spec.js b/test/unit/mocha.spec.js
--- a/test/unit/mocha.spec.js
+++ b/test/unit/mocha.spec.js
@@ -20,24 +20,49 @@ describe('Mocha', function() {
     beforeEach(function() {
       sandbox.stub(Mocha.prototype, 'useColors').returnsThis();
       sandbox.stub(utils, 'deprecate');
+      sandbox.stub(Mocha.prototype, 'timeout').returnsThis();
     });
 
-    it('should prefer "color" over "useColors"', function() {
-      // eslint-disable-next-line no-new
-      new Mocha({useColors: true, color: false});
-      expect(Mocha.prototype.useColors, 'to have a call satisfying', [false]);
+    describe('when "useColors" option is defined', function() {
+      it('should prefer "color" over "useColors"', function() {
+        // eslint-disable-next-line no-new
+        new Mocha({useColors: true, color: false});
+        expect(Mocha.prototype.useColors, 'to have a call satisfying', [
+          false
+        ]).and('was called once');
+      });
+
+      it('should assign "useColors" to "color"', function() {
+        // eslint-disable-next-line no-new
+        new Mocha({useColors: true});
+        expect(Mocha.prototype.useColors, 'to have a call satisfying', [
+          true
+        ]).and('was called once');
+      });
+
+      it('should call utils.deprecate()', function() {
+        // eslint-disable-next-line no-new
+        new Mocha({useColors: true});
+        expect(utils.deprecate, 'was called once');
+      });
     });
 
-    it('should assign "useColors" to "color"', function() {
-      // eslint-disable-next-line no-new
-      new Mocha({useColors: true});
-      expect(Mocha.prototype.useColors, 'to have a call satisfying', [true]);
+    describe('when "timeout" option is `undefined`', function() {
+      it('should not attempt to set timeout', function() {
+        // eslint-disable-next-line no-new
+        new Mocha({timeout: undefined});
+        expect(Mocha.prototype.timeout, 'was not called');
+      });
     });
 
-    it('should call utils.deprecate()', function() {
-      // eslint-disable-next-line no-new
-      new Mocha({useColors: true});
-      expect(utils.deprecate, 'was called');
+    describe('when "timeout" option is `false`', function() {
+      it('should set a timeout of 0', function() {
+        // eslint-disable-next-line no-new
+        new Mocha({timeout: false});
+        expect(Mocha.prototype.timeout, 'to have a call satisfying', [0]).and(
+          'was called once'
+        );
+      });
     });
   });