<NME> utils.spec.js
<BEF> /* global BigInt */
'use strict';

var utils = require('../../lib/utils');
var sinon = require('sinon');

describe('lib/utils', function () {
  afterEach(function () {
    sinon.restore();
  });

  describe('clean()', function () {
    it('should remove the wrapping function declaration', function () {
      expect(
        utils.clean('function  (one, two, three)  {\n//code\n}'),
        'to be',
        '//code'
      );
    });

    it('should handle newlines in the function declaration', function () {
      expect(
        utils.clean('function  (one, two, three)\n  {\n//code\n}'),
        'to be',
        '//code'
      );
    });

    it('should remove space character indentation from the function body', function () {
      expect(
        utils.clean('  //line1\n    //line2'),
        'to be',
        '//line1\n  //line2'
      );
    });

    it('should remove tab character indentation from the function body', function () {
      expect(
        utils.clean('\t//line1\n\t\t//line2'),
        'to be',
        '//line1\n\t//line2'
      );
    });

    it('should handle functions with tabs in their declarations', function () {
      expect(utils.clean('function\t(\t)\t{\n//code\n}'), 'to be', '//code');
    });

    it('should handle named functions without space after name', function () {
      expect(
        utils.clean('function withName() {\n//code\n}'),
        'to be',
        '//code'
      );
    });

    it('should handle named functions with space after name', function () {
      expect(
        utils.clean('function withName () {\n//code\n}'),
        'to be',
        '//code'
      );
    });

    it('should handle functions with no space between the end and the closing brace', function () {
      expect(utils.clean('function() {/*code*/}'), 'to be', '/*code*/');
    });

    it('should handle functions with parentheses in the same line', function () {
      expect(
        utils.clean('function() { if (true) { /* code */ } }'),
        'to be',
        'if (true) { /* code */ }'
      );
    });

    it('should handle empty functions', function () {
      expect(utils.clean('function() {}'), 'to be', '');
    });

    it('should format a single line test function', function () {
      var fn = ['function () {', '  var a = 1;', '}'].join('\n');
      expect(utils.clean(fn), 'to be', 'var a = 1;');
    });

    it('should format a multi line test indented with spaces', function () {
      // and no new lines after curly braces, shouldn't matter
      var fn = [
        'function(){  var a = 1;',
        // this one has more spaces
        '    var b = 2;',
        '  var c = 3;  }'
      ].join('\n');
      expect(utils.clean(fn), 'to be', 'var a = 1;\n  var b = 2;\nvar c = 3;');
    });

    it('should format a multi line test indented with tabs', function () {
      var fn = [
        'function (arg1, arg2)   {',
        '\tif (true) {',
        '\t\tvar a = 1;',
        '\t}',
        '}'
      ].join('\n');
      expect(utils.clean(fn), 'to be', 'if (true) {\n\tvar a = 1;\n}');
    });

    it('should format functions saved in windows style - spaces', function () {
      var fn = [
        'function (one) {',
        '   do {',
        '    "nothing";',
        '   } while (false);',
        ' }'
      ].join('\r\n');
      expect(utils.clean(fn), 'to be', 'do {\n "nothing";\n} while (false);');
    });

    it('should format functions saved in windows style - tabs', function () {
      var fn = [
        'function ( )   {',
        '\tif (false) {',
        '\t\tvar json = {',
        '\t\t\tone : 1',
        '\t\t};',
        '\t}',
        '}'
      ].join('\r\n');
      expect(
        utils.clean(fn),
        'to be',
        'if (false) {\n\tvar json = {\n\t\tone : 1\n\t};\n}'
      );
    });

    it('should format es6 arrow functions', function () {
      var fn = ['() => {', '  var a = 1;', '}'].join('\n');
      expect(utils.clean(fn), 'to be', 'var a = 1;');
    });

    it('should format es6 arrow functions with implicit return', function () {
      var fn = '() => foo()';
      expect(utils.clean(fn), 'to be', 'foo()');
    });
  });

  describe('stringify()', function () {
    var stringify = utils.stringify;

    it('should return an object representation of a string created with a String constructor', function () {
      /* eslint no-new-wrappers: off */
      expect(
        stringify(new String('foo')),
        'to be',
        '{\n  "0": "f"\n  "1": "o"\n  "2": "o"\n}'
      );
    });

    it('should return Buffer with .toJSON representation', function () {
      expect(stringify(Buffer.from([0x01])), 'to be', '[\n  1\n]');
      expect(stringify(Buffer.from([0x01, 0x02])), 'to be', '[\n  1\n  2\n]');

      expect(
        stringify(Buffer.from('ABCD')),
        'to be',
        '[\n  65\n  66\n  67\n  68\n]'
      );
    });

    it('should return Date object with .toISOString() + string prefix', function () {
      expect(
        stringify(new Date(0)),
        'to be',
        '[Date: ' + new Date(0).toISOString() + ']'
      );

      var date = new Date(); // now
      expect(stringify(date), 'to be', '[Date: ' + date.toISOString() + ']');
    });

    it('should return invalid Date object with .toString() + string prefix', function () {
      expect(
        stringify(new Date('')),
        'to be',
        '[Date: ' + new Date('').toString() + ']'
      );
    });

    describe('#Number', function () {
      it('should show the handle -0 situations', function () {
        expect(stringify(-0), 'to be', '-0');
        expect(stringify(0), 'to be', '0');
        expect(stringify('-0'), 'to be', '"-0"');
      });

      it('should work well with `NaN` and `Infinity`', function () {
        expect(stringify(NaN), 'to be', 'NaN');
        expect(stringify(Infinity), 'to be', 'Infinity');
        expect(stringify(-Infinity), 'to be', '-Infinity');
      });

      it('floats and ints', function () {
        expect(stringify(1), 'to be', '1');
        expect(stringify(1.2), 'to be', '1.2');
        expect(stringify(1e9), 'to be', '1000000000');
      });

      if (typeof BigInt === 'function') {
        it('should work with bigints when possible', function () {
          expect(stringify(BigInt(1)), 'to be', '1n');
          expect(stringify(BigInt(2)), 'to be', '2n');
        });
      }
    });

    describe('canonicalize example', function () {
      it('should represent the actual full result', function () {
        var expected = {
          str: 'string',
          int: 90,
          float: 9.99,
          boolean: false,
          nil: null,
          undef: undefined,
          regex: /^[a-z|A-Z]/,
          date: new Date(0),
          func: function () {},
          infi: Infinity,
          nan: NaN,
          zero: -0,
          buffer: Buffer.from([0x01, 0x02]),
          array: [1, 2, 3],
          empArr: [],
          matrix: [[1], [2, 3, 4]],
          object: {a: 1, b: 2},
          canObj: {a: {b: 1, c: 2}, b: {}},
          empObj: {}
        };
        expected.circular = expected; // Make `Circular` situation
        var actual = [
          '{',
          '  "array": [',
          '    1',
          '    2',
          '    3',
          '  ]',
          '  "boolean": false',
          '  "buffer": [Buffer: [',
          '    1',
          '    2',
          '  ]]',
          '  "canObj": {',
          '    "a": {',
          '      "b": 1',
          '      "c": 2',
          '    }',
          '    "b": {}',
          '  }',
          '  "circular": [Circular]',
          '  "date": [Date: 1970-01-01T00:00:00.000Z]',
          '  "empArr": []',
          '  "empObj": {}',
          '  "float": 9.99',
          '  "func": [Function]',
          '  "infi": Infinity',
          '  "int": 90',
          '  "matrix": [',
          '    [',
          '      1',
          '    ]',
          '    [',
          '      2',
          '      3',
          '      4',
          '    ]',
          '  ]',
          '  "nan": NaN',
          '  "nil": [null]',
          '  "object": {',
          '    "a": 1',
          '    "b": 2',
          '  }',
          '  "regex": /^[a-z|A-Z]/',
          '  "str": "string"',
          '  "undef": [undefined]',
          '  "zero": -0',
          '}'
        ].join('\n');
        expect(stringify(expected), 'to be', actual);
      });
    });

    it('should canonicalize the object', function () {
      var travis = {name: 'travis', age: 24};
      var travis2 = {age: 24, name: 'travis'};

      expect(stringify(travis), 'to be', stringify(travis2));
    });

    it('should handle circular structures in objects', function () {
      var travis = {name: 'travis'};
      travis.whoami = travis;

      expect(
        stringify(travis),
        'to be',
        '{\n  "name": "travis"\n  "whoami": [Circular]\n}'
      );
    });

    it('should handle circular structures in arrays', function () {
      var travis = ['travis'];
      travis.push(travis);

      expect(stringify(travis), 'to be', '[\n  "travis"\n  [Circular]\n]');
    });

    it('should handle circular structures in functions', function () {
      var travis = function () {};
      travis.fn = travis;

      expect(stringify(travis), 'to be', '{\n  "fn": [Circular]\n}');
    });

    it('should handle various non-undefined, non-null, non-object, non-array, non-date, and non-function values', function () {
      var regexp = /(?:)/;
      var regExpObj = {regexp};
      var regexpString = '/(?:)/';

      expect(
        stringify(regExpObj),
        'to be',
        '{\n  "regexp": ' + regexpString + '\n}'
      );
      expect(stringify(regexp), 'to be', regexpString);

      var number = 1;
      var numberObj = {number};
      var numberString = '1';

      expect(stringify(numberObj), 'to be', '{\n  "number": ' + number + '\n}');
      expect(stringify(number), 'to be', numberString);

      var boolean = false;
      var booleanObj = {boolean};
      var booleanString = 'false';

      expect(
        stringify(booleanObj),
        'to be',
        '{\n  "boolean": ' + boolean + '\n}'
      );
      expect(stringify(boolean), 'to be', booleanString);

      var string = 'sneepy';
      var stringObj = {string};

      expect(
        stringify(stringObj),
        'to be',
        '{\n  "string": "' + string + '"\n}'
      );
      expect(stringify(string), 'to be', JSON.stringify(string));

      var nullValue = null;
      var nullObj = {null: null};
      var nullString = '[null]';

      expect(stringify(nullObj), 'to be', '{\n  "null": [null]\n}');
      expect(stringify(nullValue), 'to be', nullString);
    });

    it('should handle arrays', function () {
      var array = ['dave', 'dave', 'dave', 'dave'];
      var arrayObj = {array};
      var arrayString = '    "dave"\n    "dave"\n    "dave"\n    "dave"';

      expect(
        stringify(arrayObj),
        'to be',
        '{\n  "array": [\n' + arrayString + '\n  ]\n}'
      );
      expect(
        stringify(array),
        'to be',
        '[' + arrayString.replace(/\s+/g, '\n  ') + '\n]'
      );
    });

    it('should handle functions', function () {
      var fn = function () {};
      var fnObj = {fn};
      var fnString = '[Function]';

      expect(stringify(fnObj), 'to be', '{\n  "fn": ' + fnString + '\n}');
      expect(stringify(fn), 'to be', '[Function]');
    });

    it('should handle empty objects', function () {
      expect(stringify({}), 'to be', '{}');
      expect(stringify({foo: {}}), 'to be', '{\n  "foo": {}\n}');
    });

    it('should handle empty arrays', function () {
      expect(stringify([]), 'to be', '[]');
      expect(stringify({foo: []}), 'to be', '{\n  "foo": []\n}');
    });

    it('should handle non-empty arrays', function () {
      expect(stringify(['a', 'b', 'c']), 'to be', '[\n  "a"\n  "b"\n  "c"\n]');
    });

    it('should handle empty functions (with no properties)', function () {
      expect(
        stringify(function () {}),
        'to be',
        '[Function]'
      );
      expect(
        stringify({foo: function () {}}),
        'to be',
        '{\n  "foo": [Function]\n}'
      );
      expect(
        stringify({foo: function () {}, bar: 'baz'}),
        'to be',
        '{\n  "bar": "baz"\n  "foo": [Function]\n}'
      );
    });

    it('should handle functions w/ properties', function () {
      var fn = function () {};
      fn.bar = 'baz';
      expect(stringify(fn), 'to be', '{\n  "bar": "baz"\n}');
      expect(
        stringify({foo: fn}),
        'to be',
        '{\n  "foo": {\n    "bar": "baz"\n  }\n}'
      );
    });

    it('should handle undefined values', function () {
      expect(
        stringify({foo: undefined}),
        'to be',
        '{\n  "foo": [undefined]\n}'
      );
      expect(
        stringify({foo: 'bar', baz: undefined}),
        'to be',
        '{\n  "baz": [undefined]\n  "foo": "bar"\n}'
      );
      expect(stringify(), 'to be', '[undefined]');
    });

    it('should recurse', function () {
      expect(
        stringify({foo: {bar: {baz: {quux: {herp: 'derp'}}}}}),
        'to be',
        '{\n  "foo": {\n    "bar": {\n      "baz": {\n        "quux": {\n          "herp": "derp"\n        }\n      }\n    }\n  }\n}'
      );
    });

    it('might get confusing', function () {
      expect(stringify(null), 'to be', '[null]');
    });

    it('should not freak out if it sees a primitive twice', function () {
      expect(
        stringify({foo: null, bar: null}),
        'to be',
        '{\n  "bar": [null]\n  "foo": [null]\n}'
      );
      expect(
        stringify({foo: 1, bar: 1}),
        'to be',
        '{\n  "bar": 1\n  "foo": 1\n}'
      );
    });

    it('should stringify dates', function () {
      var date = new Date(0);
      expect(stringify(date), 'to be', '[Date: 1970-01-01T00:00:00.000Z]');
      expect(
        stringify({date}),
        'to be',
        '{\n  "date": [Date: 1970-01-01T00:00:00.000Z]\n}'
      );
    });

    it('should handle object without an Object prototype', function () {
      var a;
      if (Object.create) {
        a = Object.create(null);
      } else {
        a = {};
      }
      a.foo = 1;

      expect(stringify(a), 'to be', '{\n  "foo": 1\n}');
    });

    // In old version node.js, Symbol is not available by default.
    if (typeof global.Symbol === 'function') {
      it('should handle Symbol', function () {
        var symbol = Symbol('value');
        expect(stringify(symbol), 'to match', /^Symbol\(value\)/);
        expect(stringify({symbol}), 'to match', /"symbol": Symbol\(value\)/);
      });
    }

    it('should handle length properties that cannot be coerced to a number', function () {
      expect(
        stringify({length: {nonBuiltinProperty: 0}}),
        'to be',
        '{\n  "length": {\n    "nonBuiltinProperty": 0\n  }\n}'
      );
      expect(
        stringify({length: 'a string where length should be'}),
        'to be',
        '{\n  "length": "a string where length should be"\n}'
      );
    });
  });

  describe('type()', function () {
    /* eslint no-extend-native: off */

    var type = utils.type;
    var toString = Object.prototype.toString;

    beforeEach(function () {
      // some JS engines such as PhantomJS 1.x exhibit this behavior
      Object.prototype.toString = function () {
        if (this === global) {
          return '[object DOMWindow]';
        }
        return toString.call(this);
      };
    });

    it('should recognize various types', function () {
      expect(type({}), 'to be', 'object');
      expect(type([]), 'to be', 'array');
      expect(type(1), 'to be', 'number');
      expect(type(Infinity), 'to be', 'number');
      expect(type(null), 'to be', 'null');
      expect(type(undefined), 'to be', 'undefined');
      expect(type(new Date()), 'to be', 'object');
      expect(type(/foo/), 'to be', 'object');
      expect(type('type'), 'to be', 'string');
      expect(type(new Error()), 'to be', 'error');
      expect(type(global), 'to be', 'object');
      expect(type(true), 'to be', 'boolean');
      expect(type(Buffer.from('ff', 'hex')), 'to be', 'object');
      expect(type(Symbol.iterator), 'to be', 'symbol');
      expect(type(new Map()), 'to be', 'object');
      expect(type(new WeakMap()), 'to be', 'object');
      expect(type(new Set()), 'to be', 'object');
      expect(type(new WeakSet()), 'to be', 'object');
      expect(
        type(async () => {}),
        'to be',
        'function'
      );
    });

    describe('when toString on null or undefined stringifies window', function () {
      it('should recognize null and undefined', function () {
        expect(type(null), 'to be', 'null');
        expect(type(undefined), 'to be', 'undefined');
      });
    });

    afterEach(function () {
      Object.prototype.toString = toString;
    });
  });

  describe('canonicalType()', function () {
    /* eslint no-extend-native: off */

    var type = utils.canonicalType;
    var toString = Object.prototype.toString;

    beforeEach(function () {
      // some JS engines such as PhantomJS 1.x exhibit this behavior
      Object.prototype.toString = function () {
        if (this === global) {
          return '[object DOMWindow]';
        }
        return toString.call(this);
      };
    });

    it('should recognize various types', function () {
      expect(type({}), 'to be', 'object');
      expect(type([]), 'to be', 'array');
      expect(type(1), 'to be', 'number');
      expect(type(Infinity), 'to be', 'number');
      expect(type(null), 'to be', 'null');
      expect(type(undefined), 'to be', 'undefined');
      expect(type(new Date()), 'to be', 'date');
      expect(type(/foo/), 'to be', 'regexp');
      expect(type('type'), 'to be', 'string');
      expect(type(global), 'to be', 'domwindow');
      expect(type(true), 'to be', 'boolean');
    });

    describe('when toString on null or undefined stringifies window', function () {
      it('should recognize null and undefined', function () {
        expect(type(null), 'to be', 'null');
        expect(type(undefined), 'to be', 'undefined');
      });
    });

    afterEach(function () {
      Object.prototype.toString = toString;
    });
  });

  describe('isPromise()', function () {
    it('should return true if the value is Promise-ish', function () {
      expect(
        utils.isPromise({
          then: function () {}
        }),
        'to be',
        true
      );
    });

    it('should return false if the value is not an object', function () {
      expect(utils.isPromise(1), 'to be', false);
    });

    it('should return false if the value is an object w/o a "then" function', function () {
      expect(utils.isPromise({}), 'to be', false);
    });

    it('should return false if the object is null', function () {
      expect(utils.isPromise(null), 'to be', false);
    });
  });

  describe('escape()', function () {
    it('replaces the usual xml suspects', function () {
      expect(utils.escape('<a<bc<d<'), 'to be', '&#x3C;a&#x3C;bc&#x3C;d&#x3C;');
      expect(utils.escape('>a>bc>d>'), 'to be', '&#x3E;a&#x3E;bc&#x3E;d&#x3E;');
      expect(utils.escape('"a"bc"d"'), 'to be', '&#x22;a&#x22;bc&#x22;d&#x22;');
      expect(utils.escape('<>"&'), 'to be', '&#x3C;&#x3E;&#x22;&#x26;');

      expect(utils.escape('&a&bc&d&'), 'to be', '&#x26;a&#x26;bc&#x26;d&#x26;');
      expect(utils.escape('&amp;&lt;'), 'to be', '&#x26;amp;&#x26;lt;');
    });

    it('replaces invalid xml characters', function () {
      expect(
        utils.escape('\x1B[32mfoo\x1B[0m'),
        'to be',
        '&#x1B;[32mfoo&#x1B;[0m'
      );
      // Ensure we can handle non-trivial unicode characters as well
      expect(utils.escape('💩'), 'to be', '&#x1F4A9;');
    });
  });

  describe('createMap()', function () {
    });
  });

  describe('sQuote()', function() {
    var str = 'xxx';

    it('should return its input as string wrapped in single quotes', function() {
      var expected = "'xxx'";
      expect(utils.sQuote(str), 'to be', expected);
    });
  });

  describe('dQuote()', function() {
    var str = 'xxx';

    it('should return its input as string wrapped in double quotes', function() {
      var expected = '"xxx"';
      expect(utils.dQuote(str), 'to be', expected);
    });
  });

  describe('createMap()', function() {
    it('should return an object with a null prototype', function() {
      expect(Object.getPrototypeOf(utils.createMap()), 'to be', null);
        foo: 'bar'
      });
    });

    it('should add props from all object parameters to the object', function () {
      expect(
        utils.createMap({foo: 'bar'}, {bar: 'baz'}),
        'to exhaustively satisfy',
        {foo: 'bar', bar: 'baz'}
      );
    });
  });

  describe('slug()', function () {
    it('should convert the string to lowercase', function () {
      expect(utils.slug('FOO'), 'to be', 'foo');
    });

    it('should convert whitespace to dashes', function () {
      expect(
        utils.slug('peanut butter\nand\tjelly'),
        'to be',
        'peanut-butter-and-jelly'
      );
    });

    it('should strip non-alphanumeric and non-dash characters', function () {
      expect(utils.slug('murder-hornets!!'), 'to be', 'murder-hornets');
    });

    it('should disallow consecutive dashes', function () {
      expect(utils.slug('poppies & fritz'), 'to be', 'poppies-fritz');
    });
  });

  describe('castArray()', function () {
    describe('when provided an array value', function () {
      it('should return a copy of the array', function () {
        const v = ['foo', 'bar', 'baz'];
        expect(utils.castArray(v), 'to equal', ['foo', 'bar', 'baz']).and(
          'not to be',
          v
        );
      });
    });

    describe('when provided an "arguments" value', function () {
      it('should return an array containing the arguments', function () {
        (function () {
          expect(utils.castArray(arguments), 'to equal', [
            'foo',
            'bar',
            'baz'
          ]).and('not to be', arguments);
        })('foo', 'bar', 'baz');
      });
    });

    describe('when provided an object', function () {
      it('should return an array containing the object only', function () {
        const v = {foo: 'bar'};
        expect(utils.castArray(v), 'to equal', [v]);
      });
    });

    describe('when provided no parameters', function () {
      it('should return an empty array', function () {
        expect(utils.castArray(), 'to equal', []);
      });
    });

    describe('when provided a primitive value', function () {
      it('should return an array containing the primitive value only', function () {
        expect(utils.castArray('butts'), 'to equal', ['butts']);
      });
    });

    describe('when provided null', function () {
      it('should return an array containing a null value only', function () {
        expect(utils.castArray(null), 'to equal', [null]);
      });
    });
  });

  describe('uniqueID()', function () {
    it('should return a non-empty string', function () {
      expect(utils.uniqueID(), 'to be a string').and('not to be empty');
    });
  });
});

<MSG> Add new option "--dry-run" (#4640)


<DFF> @@ -669,24 +669,6 @@ describe('lib/utils', function() {
     });
   });
 
-  describe('sQuote()', function() {
-    var str = 'xxx';
-
-    it('should return its input as string wrapped in single quotes', function() {
-      var expected = "'xxx'";
-      expect(utils.sQuote(str), 'to be', expected);
-    });
-  });
-
-  describe('dQuote()', function() {
-    var str = 'xxx';
-
-    it('should return its input as string wrapped in double quotes', function() {
-      var expected = '"xxx"';
-      expect(utils.dQuote(str), 'to be', expected);
-    });
-  });
-
   describe('createMap()', function() {
     it('should return an object with a null prototype', function() {
       expect(Object.getPrototypeOf(utils.createMap()), 'to be', null);

{'commit': 'diff --git a/docs/index.md b/docs/index.md\nindex 1cc80476..12b4b189 100644\n--- a/docs/index.md\n+++ b/docs/index.md\n@@ -874,6 +874,12 @@ Use this option to have Mocha check for global variables that are leaked while r\n \n > _`--compilers` was removed in v6.0.0. See [further explanation and workarounds][mocha-wiki-compilers]._\n \n+### `--dry-run`\n+\n+> _New in v9.0.0._\n+\n+Report tests without executing any of them, neither tests nor hooks.\n+\n ### `--exit`\n \n > _Updated in v4.0.0._\n@@ -2102,6 +2108,7 @@ mocha.setup({\n   asyncOnly: true,\n   bail: true,\n   checkLeaks: true,\n+  dryRun: true,\n   forbidOnly: true,\n   forbidPending: true,\n   global: [\'MyLib\'],\ndiff --git a/lib/cli/run-option-metadata.js b/lib/cli/run-option-metadata.js\nindex 33cd15ae..fc870992 100644\n--- a/lib/cli/run-option-metadata.js\n+++ b/lib/cli/run-option-metadata.js\n@@ -32,6 +32,7 @@ const TYPES = (exports.types = {\n     \'color\',\n     \'delay\',\n     \'diff\',\n+    \'dry-run\',\n     \'exit\',\n     \'forbid-only\',\n     \'forbid-pending\',\ndiff --git a/lib/cli/run.js b/lib/cli/run.js\nindex a8c8b619..79903327 100644\n--- a/lib/cli/run.js\n+++ b/lib/cli/run.js\n@@ -83,6 +83,10 @@ exports.builder = yargs =>\n         description: \'Show diff on failure\',\n         group: GROUPS.OUTPUT\n       },\n+      \'dry-run\': {\n+        description: \'Report tests without executing them\',\n+        group: GROUPS.RULES\n+      },\n       exit: {\n         description: \'Force Mocha to quit after tests complete\',\n         group: GROUPS.RULES\ndiff --git a/lib/mocha.js b/lib/mocha.js\nindex f1f2e25d..62a414a9 100644\n--- a/lib/mocha.js\n+++ b/lib/mocha.js\n@@ -30,7 +30,6 @@ const {\n   EVENT_FILE_POST_REQUIRE,\n   EVENT_FILE_REQUIRE\n } = Suite.constants;\n-var sQuote = utils.sQuote;\n var debug = require(\'debug\')(\'mocha:mocha\');\n \n exports = module.exports = Mocha;\n@@ -164,6 +163,7 @@ exports.run = function(...args) {\n  * @param {boolean} [options.color] - Color TTY output from reporter?\n  * @param {boolean} [options.delay] - Delay root suite execution?\n  * @param {boolean} [options.diff] - Show diff on failure?\n+ * @param {boolean} [options.dryRun] - Report tests without running them?\n  * @param {string} [options.fgrep] - Test filter given string.\n  * @param {boolean} [options.forbidOnly] - Tests marked `only` fail the suite?\n  * @param {boolean} [options.forbidPending] - Pending tests fail the suite?\n@@ -200,7 +200,7 @@ function Mocha(options = {}) {\n     .ui(options.ui)\n     .reporter(\n       options.reporter,\n-      options.reporterOption || options.reporterOptions // reporterOptions was previously the only way to specify options to reporter\n+      options.reporterOption || options.reporterOptions // for backwards compability\n     )\n     .slow(options.slow)\n     .global(options.global);\n@@ -222,6 +222,7 @@ function Mocha(options = {}) {\n     \'color\',\n     \'delay\',\n     \'diff\',\n+    \'dryRun\',\n     \'forbidOnly\',\n     \'forbidPending\',\n     \'fullTrace\',\n@@ -346,23 +347,19 @@ Mocha.prototype.reporter = function(reporterName, reporterOptions) {\n             reporter = require(path.resolve(utils.cwd(), reporterName));\n           } catch (_err) {\n             _err.code === \'MODULE_NOT_FOUND\'\n-              ? warn(sQuote(reporterName) + \' reporter not found\')\n+              ? warn(`\'${reporterName}\' reporter not found`)\n               : warn(\n-                  sQuote(reporterName) +\n-                    \' reporter blew up with error:\\n\' +\n-                    err.stack\n+                  `\'${reporterName}\' reporter blew up with error:\\n ${err.stack}`\n                 );\n           }\n         } else {\n-          warn(\n-            sQuote(reporterName) + \' reporter blew up with error:\\n\' + err.stack\n-          );\n+          warn(`\'${reporterName}\' reporter blew up with error:\\n ${err.stack}`);\n         }\n       }\n     }\n     if (!reporter) {\n       throw createInvalidReporterError(\n-        \'invalid reporter \' + sQuote(reporterName),\n+        `invalid reporter \'${reporterName}\'`,\n         reporterName\n       );\n     }\n@@ -396,10 +393,7 @@ Mocha.prototype.ui = function(ui) {\n       try {\n         bindInterface = require(ui);\n       } catch (err) {\n-        throw createInvalidInterfaceError(\n-          \'invalid interface \' + sQuote(ui),\n-          ui\n-        );\n+        throw createInvalidInterfaceError(`invalid interface \'${ui}\'`, ui);\n       }\n     }\n   }\n@@ -784,6 +778,20 @@ Mocha.prototype.diff = function(diff) {\n   return this;\n };\n \n+/**\n+ * Enables or disables running tests in dry-run mode.\n+ *\n+ * @public\n+ * @see [CLI option](../#-dry-run)\n+ * @param {boolean} [dryRun=true] - Whether to activate dry-run mode.\n+ * @return {Mocha} this\n+ * @chainable\n+ */\n+Mocha.prototype.dryRun = function(dryRun) {\n+  this.options.dryRun = dryRun !== false;\n+  return this;\n+};\n+\n /**\n  * @summary\n  * Sets timeout threshold value.\n@@ -1016,6 +1024,7 @@ Mocha.prototype.run = function(fn) {\n   options.files = this.files;\n   const runner = new this._runnerClass(suite, {\n     delay: options.delay,\n+    dryRun: options.dryRun,\n     cleanReferencesAfterRun: this._cleanReferencesAfterRun\n   });\n   createStatsCollector(runner);\ndiff --git a/lib/runner.js b/lib/runner.js\nindex f493004a..079ab306 100644\n--- a/lib/runner.js\n+++ b/lib/runner.js\n@@ -4,7 +4,6 @@\n  * Module dependencies.\n  * @private\n  */\n-var util = require(\'util\');\n var EventEmitter = require(\'events\').EventEmitter;\n var Pending = require(\'./pending\');\n var utils = require(\'./utils\');\n@@ -19,8 +18,6 @@ var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;\n var STATE_FAILED = Runnable.constants.STATE_FAILED;\n var STATE_PASSED = Runnable.constants.STATE_PASSED;\n var STATE_PENDING = Runnable.constants.STATE_PENDING;\n-var dQuote = utils.dQuote;\n-var sQuote = utils.sQuote;\n var stackFilter = utils.stackTraceFilter();\n var stringify = utils.stringify;\n \n@@ -140,6 +137,7 @@ class Runner extends EventEmitter {\n    * @param {Suite} suite - Root suite\n    * @param {Object|boolean} [opts] - Options. If `boolean` (deprecated), whether or not to delay execution of root suite until ready.\n    * @param {boolean} [opts.delay] - Whether to delay execution of root suite until ready.\n+   * @param {boolean} [opts.dryRun] - Whether to report tests without running them.\n    * @param {boolean} [opts.cleanReferencesAfterRun] - Whether to clean references to test fns and hooks when a suite is done.\n    */\n   constructor(suite, opts) {\n@@ -410,9 +408,8 @@ Runner.prototype.checkGlobals = function(test) {\n   this._globals = this._globals.concat(leaks);\n \n   if (leaks.length) {\n-    var msg = \'global leak(s) detected: %s\';\n-    var error = new Error(util.format(msg, leaks.map(sQuote).join(\', \')));\n-    this.fail(test, error);\n+    var msg = `global leak(s) detected: ${leaks.map(e => `\'${e}\'`).join(\', \')}`;\n+    this.fail(test, new Error(msg));\n   }\n };\n \n@@ -479,6 +476,8 @@ Runner.prototype.fail = function(test, err, force) {\n  */\n \n Runner.prototype.hook = function(name, fn) {\n+  if (this._opts.dryRun) return fn();\n+\n   var suite = this.suite;\n   var hooks = suite.getHooks(name);\n   var self = this;\n@@ -557,8 +556,7 @@ Runner.prototype.hook = function(name, fn) {\n     function setHookTitle(hook) {\n       hook.originalTitle = hook.originalTitle || hook.title;\n       if (hook.ctx && hook.ctx.currentTest) {\n-        hook.title =\n-          hook.originalTitle + \' for \' + dQuote(hook.ctx.currentTest.title);\n+        hook.title = `${hook.originalTitle} for "${hook.ctx.currentTest.title}"`;\n       } else {\n         var parentTitle;\n         if (hook.parent.title) {\n@@ -566,7 +564,7 @@ Runner.prototype.hook = function(name, fn) {\n         } else {\n           parentTitle = hook.parent.root ? \'{root}\' : \'\';\n         }\n-        hook.title = hook.originalTitle + \' in \' + dQuote(parentTitle);\n+        hook.title = `${hook.originalTitle} in "${parentTitle}"`;\n       }\n     }\n   }\n@@ -612,7 +610,7 @@ Runner.prototype.hooks = function(name, suites, fn) {\n };\n \n /**\n- * Run hooks from the top level down.\n+ * Run \'afterEach\' hooks from bottom up.\n  *\n  * @param {String} name\n  * @param {Function} fn\n@@ -624,7 +622,7 @@ Runner.prototype.hookUp = function(name, fn) {\n };\n \n /**\n- * Run hooks from the bottom up.\n+ * Run \'beforeEach\' hooks from top level down.\n  *\n  * @param {String} name\n  * @param {Function} fn\n@@ -659,6 +657,8 @@ Runner.prototype.parents = function() {\n  * @private\n  */\n Runner.prototype.runTest = function(fn) {\n+  if (this._opts.dryRun) return fn();\n+\n   var self = this;\n   var test = this.test;\n \n@@ -704,7 +704,6 @@ Runner.prototype.runTests = function(suite, fn) {\n     self.suite = after ? errSuite.parent : errSuite;\n \n     if (self.suite) {\n-      // call hookUp afterEach\n       self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {\n         self.suite = orig;\n         // some hooks may fail even now\ndiff --git a/lib/utils.js b/lib/utils.js\nindex e46eac8d..91203475 100644\n--- a/lib/utils.js\n+++ b/lib/utils.js\n@@ -518,44 +518,6 @@ exports.clamp = function clamp(value, range) {\n   return Math.min(Math.max(value, range[0]), range[1]);\n };\n \n-/**\n- * Single quote text by combining with undirectional ASCII quotation marks.\n- *\n- * @description\n- * Provides a simple means of markup for quoting text to be used in output.\n- * Use this to quote names of variables, methods, and packages.\n- *\n- * <samp>package \'foo\' cannot be found</samp>\n- *\n- * @private\n- * @param {string} str - Value to be quoted.\n- * @returns {string} quoted value\n- * @example\n- * sQuote(\'n\') // => \'n\'\n- */\n-exports.sQuote = function(str) {\n-  return "\'" + str + "\'";\n-};\n-\n-/**\n- * Double quote text by combining with undirectional ASCII quotation marks.\n- *\n- * @description\n- * Provides a simple means of markup for quoting text to be used in output.\n- * Use this to quote names of datatypes, classes, pathnames, and strings.\n- *\n- * <samp>argument \'value\' must be "string" or "number"</samp>\n- *\n- * @private\n- * @param {string} str - Value to be quoted.\n- * @returns {string} quoted value\n- * @example\n- * dQuote(\'number\') // => "number"\n- */\n-exports.dQuote = function(str) {\n-  return \'"\' + str + \'"\';\n-};\n-\n /**\n  * It\'s a noop.\n  * @public\ndiff --git a/test/integration/fixtures/options/dry-run/dry-run.fixture.js b/test/integration/fixtures/options/dry-run/dry-run.fixture.js\nnew file mode 100644\nindex 00000000..ebb48ad1\n--- /dev/null\n+++ b/test/integration/fixtures/options/dry-run/dry-run.fixture.js\n@@ -0,0 +1,35 @@\n+\'use strict\';\n+\n+describe.only(\'suite1\', function() {\n+  it.skip(\'test1 - report as skipped\', function() { });\n+\n+  it(\'test2 - report as passed\', function() { });\n+\n+  it(\'test3 - report as passed\', function() {\n+    throw new Error(\'this test should not run\');\n+  });\n+});\n+\n+describe(\'suite2\', function () {\n+  before(function() {\n+    throw new Error(\'this hook should not run\');\n+  });\n+  beforeEach(function() {\n+    throw new Error(\'this hook should not run\');\n+  });\n+\n+  it.only(\'test4 - report as passed\', function () {\n+    throw new Error(\'this test should not run\');\n+  });\n+\n+  it(\'test5 - should be ignored\', function () {\n+    throw new Error(\'this test should not run\');\n+  });\n+\n+  afterEach(function() {\n+    throw new Error(\'this hook should not run\');\n+  });\n+  after(function() {\n+    throw new Error(\'this hook should not run\');\n+  });\n+});\ndiff --git a/test/integration/options/dryRun.spec.js b/test/integration/options/dryRun.spec.js\nnew file mode 100644\nindex 00000000..81325948\n--- /dev/null\n+++ b/test/integration/options/dryRun.spec.js\n@@ -0,0 +1,30 @@\n+\'use strict\';\n+\n+var path = require(\'path\').posix;\n+var helpers = require(\'../helpers\');\n+var runMochaJSON = helpers.runMochaJSON;\n+\n+describe(\'--dry-run\', function() {\n+  var args = [\'--dry-run\'];\n+\n+  it(\'should only report, but not execute any test\', function(done) {\n+    var fixture = path.join(\'options/dry-run\', \'dry-run\');\n+    runMochaJSON(fixture, args, function(err, res) {\n+      if (err) {\n+        return done(err);\n+      }\n+\n+      expect(res, \'to have passed\')\n+        .and(\n+          \'to have passed tests\',\n+          \'test2 - report as passed\',\n+          \'test3 - report as passed\',\n+          \'test4 - report as passed\'\n+        )\n+        .and(\'to have passed test count\', 3)\n+        .and(\'to have pending test count\', 1)\n+        .and(\'to have failed test count\', 0);\n+      done();\n+    });\n+  });\n+});\ndiff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js\nindex 944f94f5..17bbcd2f 100644\n--- a/test/integration/reporters.spec.js\n+++ b/test/integration/reporters.spec.js\n@@ -5,8 +5,6 @@ var fs = require(\'fs\');\n var crypto = require(\'crypto\');\n var path = require(\'path\');\n var run = require(\'./helpers\').runMocha;\n-var utils = require(\'../../lib/utils\');\n-var dQuote = utils.dQuote;\n \n describe(\'reporters\', function() {\n   describe(\'markdown\', function() {\n@@ -215,9 +213,7 @@ describe(\'reporters\', function() {\n             return;\n           }\n \n-          var pattern =\n-            \'^Error: invalid or unsupported TAP version: \' +\n-            dQuote(invalidTapVersion);\n+          var pattern = `^Error: invalid or unsupported TAP version: "${invalidTapVersion}"`;\n           expect(res, \'to satisfy\', {\n             code: 1,\n             output: new RegExp(pattern, \'m\')\ndiff --git a/test/reporters/json-stream.spec.js b/test/reporters/json-stream.spec.js\nindex 58694e3f..f4610200 100644\n--- a/test/reporters/json-stream.spec.js\n+++ b/test/reporters/json-stream.spec.js\n@@ -3,11 +3,9 @@\n var events = require(\'../../\').Runner.constants;\n var helpers = require(\'./helpers\');\n var reporters = require(\'../../\').reporters;\n-var utils = require(\'../../lib/utils\');\n \n var JSONStream = reporters.JSONStream;\n var createMockRunner = helpers.createMockRunner;\n-var dQuote = utils.dQuote;\n var makeExpectedTest = helpers.makeExpectedTest;\n var makeRunReporter = helpers.createRunReporterFunction;\n \n@@ -71,17 +69,17 @@ describe(\'JSON Stream reporter\', function() {\n           stdout[0],\n           \'to equal\',\n           \'["pass",{"title":\' +\n-            dQuote(expectedTitle) +\n+            `"${expectedTitle}"` +\n             \',"fullTitle":\' +\n-            dQuote(expectedFullTitle) +\n+            `"${expectedFullTitle}"` +\n             \',"file":\' +\n-            dQuote(expectedFile) +\n+            `"${expectedFile}"` +\n             \',"duration":\' +\n             expectedDuration +\n             \',"currentRetry":\' +\n             currentRetry +\n             \',"speed":\' +\n-            dQuote(expectedSpeed) +\n+            `"${expectedSpeed}"` +\n             \'}]\\n\'\n         );\n       });\n@@ -106,21 +104,21 @@ describe(\'JSON Stream reporter\', function() {\n             stdout[0],\n             \'to equal\',\n             \'["fail",{"title":\' +\n-              dQuote(expectedTitle) +\n+              `"${expectedTitle}"` +\n               \',"fullTitle":\' +\n-              dQuote(expectedFullTitle) +\n+              `"${expectedFullTitle}"` +\n               \',"file":\' +\n-              dQuote(expectedFile) +\n+              `"${expectedFile}"` +\n               \',"duration":\' +\n               expectedDuration +\n               \',"currentRetry":\' +\n               currentRetry +\n               \',"speed":\' +\n-              dQuote(expectedSpeed) +\n+              `"${expectedSpeed}"` +\n               \',"err":\' +\n-              dQuote(expectedErrorMessage) +\n+              `"${expectedErrorMessage}"` +\n               \',"stack":\' +\n-              dQuote(expectedErrorStack) +\n+              `"${expectedErrorStack}"` +\n               \'}]\\n\'\n           );\n         });\n@@ -144,19 +142,19 @@ describe(\'JSON Stream reporter\', function() {\n             stdout[0],\n             \'to equal\',\n             \'["fail",{"title":\' +\n-              dQuote(expectedTitle) +\n+              `"${expectedTitle}"` +\n               \',"fullTitle":\' +\n-              dQuote(expectedFullTitle) +\n+              `"${expectedFullTitle}"` +\n               \',"file":\' +\n-              dQuote(expectedFile) +\n+              `"${expectedFile}"` +\n               \',"duration":\' +\n               expectedDuration +\n               \',"currentRetry":\' +\n               currentRetry +\n               \',"speed":\' +\n-              dQuote(expectedSpeed) +\n+              `"${expectedSpeed}"` +\n               \',"err":\' +\n-              dQuote(expectedErrorMessage) +\n+              `"${expectedErrorMessage}"` +\n               \',"stack":null}]\\n\'\n           );\n         });\ndiff --git a/test/unit/mocha.spec.js b/test/unit/mocha.spec.js\nindex 142018b5..35f7abd3 100644\n--- a/test/unit/mocha.spec.js\n+++ b/test/unit/mocha.spec.js\n@@ -352,6 +352,20 @@ describe(\'Mocha\', function() {\n       });\n     });\n \n+    describe(\'dryRun()\', function() {\n+      it(\'should set the dryRun option to true\', function() {\n+        mocha.dryRun();\n+        expect(mocha.options, \'to have property\', \'dryRun\', true);\n+      });\n+\n+      describe(\'when provided `false` argument\', function() {\n+        it(\'should set the dryRun option to false\', function() {\n+          mocha.dryRun(false);\n+          expect(mocha.options, \'to have property\', \'dryRun\', false);\n+        });\n+      });\n+    });\n+\n     describe(\'dispose()\', function() {\n       it(\'should dispose the root suite\', function() {\n         mocha.dispose();\ndiff --git a/test/unit/utils.spec.js b/test/unit/utils.spec.js\nindex 53d4f20f..9e4068b9 100644\n--- a/test/unit/utils.spec.js\n+++ b/test/unit/utils.spec.js\n@@ -669,24 +669,6 @@ describe(\'lib/utils\', function() {\n     });\n   });\n \n-  describe(\'sQuote()\', function() {\n-    var str = \'xxx\';\n-\n-    it(\'should return its input as string wrapped in single quotes\', function() {\n-      var expected = "\'xxx\'";\n-      expect(utils.sQuote(str), \'to be\', expected);\n-    });\n-  });\n-\n-  describe(\'dQuote()\', function() {\n-    var str = \'xxx\';\n-\n-    it(\'should return its input as string wrapped in double quotes\', function() {\n-      var expected = \'"xxx"\';\n-      expect(utils.dQuote(str), \'to be\', expected);\n-    });\n-  });\n-\n   describe(\'createMap()\', function() {\n     it(\'should return an object with a null prototype\', function() {\n       expect(Object.getPrototypeOf(utils.createMap()), \'to be\', null);\n', 'commit_subject': 'Add new option "--dry-run" (#4640)', 'repo_name': 'mochajs/mocha', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1622883589', 'tz_offset': '120', 'date': {'seconds': '1622883589'}}, 'commit_tree': '545d21963d737bcf2f0de21bc8fb0c180795d4e6', 'commit_message': 'Add new option "--dry-run" (#4640)\n\n', 'language_name': 'JavaScript', 'license': 'mit', 'hunks': '@@ -669,24 +669,6 @@ describe(\'lib/utils\', function() {\n     });\n   });\n \n-  describe(\'sQuote()\', function() {\n-    var str = \'xxx\';\n-\n-    it(\'should return its input as string wrapped in single quotes\', function() {\n-      var expected = "\'xxx\'";\n-      expect(utils.sQuote(str), \'to be\', expected);\n-    });\n-  });\n-\n-  describe(\'dQuote()\', function() {\n-    var str = \'xxx\';\n-\n-    it(\'should return its input as string wrapped in double quotes\', function() {\n-      var expected = \'"xxx"\';\n-      expect(utils.dQuote(str), \'to be\', expected);\n-    });\n-  });\n-\n   describe(\'createMap()\', function() {\n     it(\'should return an object with a null prototype\', function() {\n       expect(Object.getPrototypeOf(utils.createMap()), \'to be\', null);\n', 'addition_count': 0, 'deletion_count': 18, 'src_file': 'a/test/unit/utils.spec.js', 'tgt_file': 'b/test/unit/utils.spec.js', 'file_extension': '.js', 'before_file': '/* global BigInt */\n\'use strict\';\n\nvar utils = require(\'../../lib/utils\');\nvar sinon = require(\'sinon\');\n\ndescribe(\'lib/utils\', function () {\n  afterEach(function () {\n    sinon.restore();\n  });\n\n  describe(\'clean()\', function () {\n    it(\'should remove the wrapping function declaration\', function () {\n      expect(\n        utils.clean(\'function  (one, two, three)  {\\n//code\\n}\'),\n        \'to be\',\n        \'//code\'\n      );\n    });\n\n    it(\'should handle newlines in the function declaration\', function () {\n      expect(\n        utils.clean(\'function  (one, two, three)\\n  {\\n//code\\n}\'),\n        \'to be\',\n        \'//code\'\n      );\n    });\n\n    it(\'should remove space character indentation from the function body\', function () {\n      expect(\n        utils.clean(\'  //line1\\n    //line2\'),\n        \'to be\',\n        \'//line1\\n  //line2\'\n      );\n    });\n\n    it(\'should remove tab character indentation from the function body\', function () {\n      expect(\n        utils.clean(\'\\t//line1\\n\\t\\t//line2\'),\n        \'to be\',\n        \'//line1\\n\\t//line2\'\n      );\n    });\n\n    it(\'should handle functions with tabs in their declarations\', function () {\n      expect(utils.clean(\'function\\t(\\t)\\t{\\n//code\\n}\'), \'to be\', \'//code\');\n    });\n\n    it(\'should handle named functions without space after name\', function () {\n      expect(\n        utils.clean(\'function withName() {\\n//code\\n}\'),\n        \'to be\',\n        \'//code\'\n      );\n    });\n\n    it(\'should handle named functions with space after name\', function () {\n      expect(\n        utils.clean(\'function withName () {\\n//code\\n}\'),\n        \'to be\',\n        \'//code\'\n      );\n    });\n\n    it(\'should handle functions with no space between the end and the closing brace\', function () {\n      expect(utils.clean(\'function() {/*code*/}\'), \'to be\', \'/*code*/\');\n    });\n\n    it(\'should handle functions with parentheses in the same line\', function () {\n      expect(\n        utils.clean(\'function() { if (true) { /* code */ } }\'),\n        \'to be\',\n        \'if (true) { /* code */ }\'\n      );\n    });\n\n    it(\'should handle empty functions\', function () {\n      expect(utils.clean(\'function() {}\'), \'to be\', \'\');\n    });\n\n    it(\'should format a single line test function\', function () {\n      var fn = [\'function () {\', \'  var a = 1;\', \'}\'].join(\'\\n\');\n      expect(utils.clean(fn), \'to be\', \'var a = 1;\');\n    });\n\n    it(\'should format a multi line test indented with spaces\', function () {\n      // and no new lines after curly braces, shouldn\'t matter\n      var fn = [\n        \'function(){  var a = 1;\',\n        // this one has more spaces\n        \'    var b = 2;\',\n        \'  var c = 3;  }\'\n      ].join(\'\\n\');\n      expect(utils.clean(fn), \'to be\', \'var a = 1;\\n  var b = 2;\\nvar c = 3;\');\n    });\n\n    it(\'should format a multi line test indented with tabs\', function () {\n      var fn = [\n        \'function (arg1, arg2)   {\',\n        \'\\tif (true) {\',\n        \'\\t\\tvar a = 1;\',\n        \'\\t}\',\n        \'}\'\n      ].join(\'\\n\');\n      expect(utils.clean(fn), \'to be\', \'if (true) {\\n\\tvar a = 1;\\n}\');\n    });\n\n    it(\'should format functions saved in windows style - spaces\', function () {\n      var fn = [\n        \'function (one) {\',\n        \'   do {\',\n        \'    "nothing";\',\n        \'   } while (false);\',\n        \' }\'\n      ].join(\'\\r\\n\');\n      expect(utils.clean(fn), \'to be\', \'do {\\n "nothing";\\n} while (false);\');\n    });\n\n    it(\'should format functions saved in windows style - tabs\', function () {\n      var fn = [\n        \'function ( )   {\',\n        \'\\tif (false) {\',\n        \'\\t\\tvar json = {\',\n        \'\\t\\t\\tone : 1\',\n        \'\\t\\t};\',\n        \'\\t}\',\n        \'}\'\n      ].join(\'\\r\\n\');\n      expect(\n        utils.clean(fn),\n        \'to be\',\n        \'if (false) {\\n\\tvar json = {\\n\\t\\tone : 1\\n\\t};\\n}\'\n      );\n    });\n\n    it(\'should format es6 arrow functions\', function () {\n      var fn = [\'() => {\', \'  var a = 1;\', \'}\'].join(\'\\n\');\n      expect(utils.clean(fn), \'to be\', \'var a = 1;\');\n    });\n\n    it(\'should format es6 arrow functions with implicit return\', function () {\n      var fn = \'() => foo()\';\n      expect(utils.clean(fn), \'to be\', \'foo()\');\n    });\n  });\n\n  describe(\'stringify()\', function () {\n    var stringify = utils.stringify;\n\n    it(\'should return an object representation of a string created with a String constructor\', function () {\n      /* eslint no-new-wrappers: off */\n      expect(\n        stringify(new String(\'foo\')),\n        \'to be\',\n        \'{\\n  "0": "f"\\n  "1": "o"\\n  "2": "o"\\n}\'\n      );\n    });\n\n    it(\'should return Buffer with .toJSON representation\', function () {\n      expect(stringify(Buffer.from([0x01])), \'to be\', \'[\\n  1\\n]\');\n      expect(stringify(Buffer.from([0x01, 0x02])), \'to be\', \'[\\n  1\\n  2\\n]\');\n\n      expect(\n        stringify(Buffer.from(\'ABCD\')),\n        \'to be\',\n        \'[\\n  65\\n  66\\n  67\\n  68\\n]\'\n      );\n    });\n\n    it(\'should return Date object with .toISOString() + string prefix\', function () {\n      expect(\n        stringify(new Date(0)),\n        \'to be\',\n        \'[Date: \' + new Date(0).toISOString() + \']\'\n      );\n\n      var date = new Date(); // now\n      expect(stringify(date), \'to be\', \'[Date: \' + date.toISOString() + \']\');\n    });\n\n    it(\'should return invalid Date object with .toString() + string prefix\', function () {\n      expect(\n        stringify(new Date(\'\')),\n        \'to be\',\n        \'[Date: \' + new Date(\'\').toString() + \']\'\n      );\n    });\n\n    describe(\'#Number\', function () {\n      it(\'should show the handle -0 situations\', function () {\n        expect(stringify(-0), \'to be\', \'-0\');\n        expect(stringify(0), \'to be\', \'0\');\n        expect(stringify(\'-0\'), \'to be\', \'"-0"\');\n      });\n\n      it(\'should work well with `NaN` and `Infinity`\', function () {\n        expect(stringify(NaN), \'to be\', \'NaN\');\n        expect(stringify(Infinity), \'to be\', \'Infinity\');\n        expect(stringify(-Infinity), \'to be\', \'-Infinity\');\n      });\n\n      it(\'floats and ints\', function () {\n        expect(stringify(1), \'to be\', \'1\');\n        expect(stringify(1.2), \'to be\', \'1.2\');\n        expect(stringify(1e9), \'to be\', \'1000000000\');\n      });\n\n      if (typeof BigInt === \'function\') {\n        it(\'should work with bigints when possible\', function () {\n          expect(stringify(BigInt(1)), \'to be\', \'1n\');\n          expect(stringify(BigInt(2)), \'to be\', \'2n\');\n        });\n      }\n    });\n\n    describe(\'canonicalize example\', function () {\n      it(\'should represent the actual full result\', function () {\n        var expected = {\n          str: \'string\',\n          int: 90,\n          float: 9.99,\n          boolean: false,\n          nil: null,\n          undef: undefined,\n          regex: /^[a-z|A-Z]/,\n          date: new Date(0),\n          func: function () {},\n          infi: Infinity,\n          nan: NaN,\n          zero: -0,\n          buffer: Buffer.from([0x01, 0x02]),\n          array: [1, 2, 3],\n          empArr: [],\n          matrix: [[1], [2, 3, 4]],\n          object: {a: 1, b: 2},\n          canObj: {a: {b: 1, c: 2}, b: {}},\n          empObj: {}\n        };\n        expected.circular = expected; // Make `Circular` situation\n        var actual = [\n          \'{\',\n          \'  "array": [\',\n          \'    1\',\n          \'    2\',\n          \'    3\',\n          \'  ]\',\n          \'  "boolean": false\',\n          \'  "buffer": [Buffer: [\',\n          \'    1\',\n          \'    2\',\n          \'  ]]\',\n          \'  "canObj": {\',\n          \'    "a": {\',\n          \'      "b": 1\',\n          \'      "c": 2\',\n          \'    }\',\n          \'    "b": {}\',\n          \'  }\',\n          \'  "circular": [Circular]\',\n          \'  "date": [Date: 1970-01-01T00:00:00.000Z]\',\n          \'  "empArr": []\',\n          \'  "empObj": {}\',\n          \'  "float": 9.99\',\n          \'  "func": [Function]\',\n          \'  "infi": Infinity\',\n          \'  "int": 90\',\n          \'  "matrix": [\',\n          \'    [\',\n          \'      1\',\n          \'    ]\',\n          \'    [\',\n          \'      2\',\n          \'      3\',\n          \'      4\',\n          \'    ]\',\n          \'  ]\',\n          \'  "nan": NaN\',\n          \'  "nil": [null]\',\n          \'  "object": {\',\n          \'    "a": 1\',\n          \'    "b": 2\',\n          \'  }\',\n          \'  "regex": /^[a-z|A-Z]/\',\n          \'  "str": "string"\',\n          \'  "undef": [undefined]\',\n          \'  "zero": -0\',\n          \'}\'\n        ].join(\'\\n\');\n        expect(stringify(expected), \'to be\', actual);\n      });\n    });\n\n    it(\'should canonicalize the object\', function () {\n      var travis = {name: \'travis\', age: 24};\n      var travis2 = {age: 24, name: \'travis\'};\n\n      expect(stringify(travis), \'to be\', stringify(travis2));\n    });\n\n    it(\'should handle circular structures in objects\', function () {\n      var travis = {name: \'travis\'};\n      travis.whoami = travis;\n\n      expect(\n        stringify(travis),\n        \'to be\',\n        \'{\\n  "name": "travis"\\n  "whoami": [Circular]\\n}\'\n      );\n    });\n\n    it(\'should handle circular structures in arrays\', function () {\n      var travis = [\'travis\'];\n      travis.push(travis);\n\n      expect(stringify(travis), \'to be\', \'[\\n  "travis"\\n  [Circular]\\n]\');\n    });\n\n    it(\'should handle circular structures in functions\', function () {\n      var travis = function () {};\n      travis.fn = travis;\n\n      expect(stringify(travis), \'to be\', \'{\\n  "fn": [Circular]\\n}\');\n    });\n\n    it(\'should handle various non-undefined, non-null, non-object, non-array, non-date, and non-function values\', function () {\n      var regexp = /(?:)/;\n      var regExpObj = {regexp};\n      var regexpString = \'/(?:)/\';\n\n      expect(\n        stringify(regExpObj),\n        \'to be\',\n        \'{\\n  "regexp": \' + regexpString + \'\\n}\'\n      );\n      expect(stringify(regexp), \'to be\', regexpString);\n\n      var number = 1;\n      var numberObj = {number};\n      var numberString = \'1\';\n\n      expect(stringify(numberObj), \'to be\', \'{\\n  "number": \' + number + \'\\n}\');\n      expect(stringify(number), \'to be\', numberString);\n\n      var boolean = false;\n      var booleanObj = {boolean};\n      var booleanString = \'false\';\n\n      expect(\n        stringify(booleanObj),\n        \'to be\',\n        \'{\\n  "boolean": \' + boolean + \'\\n}\'\n      );\n      expect(stringify(boolean), \'to be\', booleanString);\n\n      var string = \'sneepy\';\n      var stringObj = {string};\n\n      expect(\n        stringify(stringObj),\n        \'to be\',\n        \'{\\n  "string": "\' + string + \'"\\n}\'\n      );\n      expect(stringify(string), \'to be\', JSON.stringify(string));\n\n      var nullValue = null;\n      var nullObj = {null: null};\n      var nullString = \'[null]\';\n\n      expect(stringify(nullObj), \'to be\', \'{\\n  "null": [null]\\n}\');\n      expect(stringify(nullValue), \'to be\', nullString);\n    });\n\n    it(\'should handle arrays\', function () {\n      var array = [\'dave\', \'dave\', \'dave\', \'dave\'];\n      var arrayObj = {array};\n      var arrayString = \'    "dave"\\n    "dave"\\n    "dave"\\n    "dave"\';\n\n      expect(\n        stringify(arrayObj),\n        \'to be\',\n        \'{\\n  "array": [\\n\' + arrayString + \'\\n  ]\\n}\'\n      );\n      expect(\n        stringify(array),\n        \'to be\',\n        \'[\' + arrayString.replace(/\\s+/g, \'\\n  \') + \'\\n]\'\n      );\n    });\n\n    it(\'should handle functions\', function () {\n      var fn = function () {};\n      var fnObj = {fn};\n      var fnString = \'[Function]\';\n\n      expect(stringify(fnObj), \'to be\', \'{\\n  "fn": \' + fnString + \'\\n}\');\n      expect(stringify(fn), \'to be\', \'[Function]\');\n    });\n\n    it(\'should handle empty objects\', function () {\n      expect(stringify({}), \'to be\', \'{}\');\n      expect(stringify({foo: {}}), \'to be\', \'{\\n  "foo": {}\\n}\');\n    });\n\n    it(\'should handle empty arrays\', function () {\n      expect(stringify([]), \'to be\', \'[]\');\n      expect(stringify({foo: []}), \'to be\', \'{\\n  "foo": []\\n}\');\n    });\n\n    it(\'should handle non-empty arrays\', function () {\n      expect(stringify([\'a\', \'b\', \'c\']), \'to be\', \'[\\n  "a"\\n  "b"\\n  "c"\\n]\');\n    });\n\n    it(\'should handle empty functions (with no properties)\', function () {\n      expect(\n        stringify(function () {}),\n        \'to be\',\n        \'[Function]\'\n      );\n      expect(\n        stringify({foo: function () {}}),\n        \'to be\',\n        \'{\\n  "foo": [Function]\\n}\'\n      );\n      expect(\n        stringify({foo: function () {}, bar: \'baz\'}),\n        \'to be\',\n        \'{\\n  "bar": "baz"\\n  "foo": [Function]\\n}\'\n      );\n    });\n\n    it(\'should handle functions w/ properties\', function () {\n      var fn = function () {};\n      fn.bar = \'baz\';\n      expect(stringify(fn), \'to be\', \'{\\n  "bar": "baz"\\n}\');\n      expect(\n        stringify({foo: fn}),\n        \'to be\',\n        \'{\\n  "foo": {\\n    "bar": "baz"\\n  }\\n}\'\n      );\n    });\n\n    it(\'should handle undefined values\', function () {\n      expect(\n        stringify({foo: undefined}),\n        \'to be\',\n        \'{\\n  "foo": [undefined]\\n}\'\n      );\n      expect(\n        stringify({foo: \'bar\', baz: undefined}),\n        \'to be\',\n        \'{\\n  "baz": [undefined]\\n  "foo": "bar"\\n}\'\n      );\n      expect(stringify(), \'to be\', \'[undefined]\');\n    });\n\n    it(\'should recurse\', function () {\n      expect(\n        stringify({foo: {bar: {baz: {quux: {herp: \'derp\'}}}}}),\n        \'to be\',\n        \'{\\n  "foo": {\\n    "bar": {\\n      "baz": {\\n        "quux": {\\n          "herp": "derp"\\n        }\\n      }\\n    }\\n  }\\n}\'\n      );\n    });\n\n    it(\'might get confusing\', function () {\n      expect(stringify(null), \'to be\', \'[null]\');\n    });\n\n    it(\'should not freak out if it sees a primitive twice\', function () {\n      expect(\n        stringify({foo: null, bar: null}),\n        \'to be\',\n        \'{\\n  "bar": [null]\\n  "foo": [null]\\n}\'\n      );\n      expect(\n        stringify({foo: 1, bar: 1}),\n        \'to be\',\n        \'{\\n  "bar": 1\\n  "foo": 1\\n}\'\n      );\n    });\n\n    it(\'should stringify dates\', function () {\n      var date = new Date(0);\n      expect(stringify(date), \'to be\', \'[Date: 1970-01-01T00:00:00.000Z]\');\n      expect(\n        stringify({date}),\n        \'to be\',\n        \'{\\n  "date": [Date: 1970-01-01T00:00:00.000Z]\\n}\'\n      );\n    });\n\n    it(\'should handle object without an Object prototype\', function () {\n      var a;\n      if (Object.create) {\n        a = Object.create(null);\n      } else {\n        a = {};\n      }\n      a.foo = 1;\n\n      expect(stringify(a), \'to be\', \'{\\n  "foo": 1\\n}\');\n    });\n\n    // In old version node.js, Symbol is not available by default.\n    if (typeof global.Symbol === \'function\') {\n      it(\'should handle Symbol\', function () {\n        var symbol = Symbol(\'value\');\n        expect(stringify(symbol), \'to match\', /^Symbol\\(value\\)/);\n        expect(stringify({symbol}), \'to match\', /"symbol": Symbol\\(value\\)/);\n      });\n    }\n\n    it(\'should handle length properties that cannot be coerced to a number\', function () {\n      expect(\n        stringify({length: {nonBuiltinProperty: 0}}),\n        \'to be\',\n        \'{\\n  "length": {\\n    "nonBuiltinProperty": 0\\n  }\\n}\'\n      );\n      expect(\n        stringify({length: \'a string where length should be\'}),\n        \'to be\',\n        \'{\\n  "length": "a string where length should be"\\n}\'\n      );\n    });\n  });\n\n  describe(\'type()\', function () {\n    /* eslint no-extend-native: off */\n\n    var type = utils.type;\n    var toString = Object.prototype.toString;\n\n    beforeEach(function () {\n      // some JS engines such as PhantomJS 1.x exhibit this behavior\n      Object.prototype.toString = function () {\n        if (this === global) {\n          return \'[object DOMWindow]\';\n        }\n        return toString.call(this);\n      };\n    });\n\n    it(\'should recognize various types\', function () {\n      expect(type({}), \'to be\', \'object\');\n      expect(type([]), \'to be\', \'array\');\n      expect(type(1), \'to be\', \'number\');\n      expect(type(Infinity), \'to be\', \'number\');\n      expect(type(null), \'to be\', \'null\');\n      expect(type(undefined), \'to be\', \'undefined\');\n      expect(type(new Date()), \'to be\', \'object\');\n      expect(type(/foo/), \'to be\', \'object\');\n      expect(type(\'type\'), \'to be\', \'string\');\n      expect(type(new Error()), \'to be\', \'error\');\n      expect(type(global), \'to be\', \'object\');\n      expect(type(true), \'to be\', \'boolean\');\n      expect(type(Buffer.from(\'ff\', \'hex\')), \'to be\', \'object\');\n      expect(type(Symbol.iterator), \'to be\', \'symbol\');\n      expect(type(new Map()), \'to be\', \'object\');\n      expect(type(new WeakMap()), \'to be\', \'object\');\n      expect(type(new Set()), \'to be\', \'object\');\n      expect(type(new WeakSet()), \'to be\', \'object\');\n      expect(\n        type(async () => {}),\n        \'to be\',\n        \'function\'\n      );\n    });\n\n    describe(\'when toString on null or undefined stringifies window\', function () {\n      it(\'should recognize null and undefined\', function () {\n        expect(type(null), \'to be\', \'null\');\n        expect(type(undefined), \'to be\', \'undefined\');\n      });\n    });\n\n    afterEach(function () {\n      Object.prototype.toString = toString;\n    });\n  });\n\n  describe(\'canonicalType()\', function () {\n    /* eslint no-extend-native: off */\n\n    var type = utils.canonicalType;\n    var toString = Object.prototype.toString;\n\n    beforeEach(function () {\n      // some JS engines such as PhantomJS 1.x exhibit this behavior\n      Object.prototype.toString = function () {\n        if (this === global) {\n          return \'[object DOMWindow]\';\n        }\n        return toString.call(this);\n      };\n    });\n\n    it(\'should recognize various types\', function () {\n      expect(type({}), \'to be\', \'object\');\n      expect(type([]), \'to be\', \'array\');\n      expect(type(1), \'to be\', \'number\');\n      expect(type(Infinity), \'to be\', \'number\');\n      expect(type(null), \'to be\', \'null\');\n      expect(type(undefined), \'to be\', \'undefined\');\n      expect(type(new Date()), \'to be\', \'date\');\n      expect(type(/foo/), \'to be\', \'regexp\');\n      expect(type(\'type\'), \'to be\', \'string\');\n      expect(type(global), \'to be\', \'domwindow\');\n      expect(type(true), \'to be\', \'boolean\');\n    });\n\n    describe(\'when toString on null or undefined stringifies window\', function () {\n      it(\'should recognize null and undefined\', function () {\n        expect(type(null), \'to be\', \'null\');\n        expect(type(undefined), \'to be\', \'undefined\');\n      });\n    });\n\n    afterEach(function () {\n      Object.prototype.toString = toString;\n    });\n  });\n\n  describe(\'isPromise()\', function () {\n    it(\'should return true if the value is Promise-ish\', function () {\n      expect(\n        utils.isPromise({\n          then: function () {}\n        }),\n        \'to be\',\n        true\n      );\n    });\n\n    it(\'should return false if the value is not an object\', function () {\n      expect(utils.isPromise(1), \'to be\', false);\n    });\n\n    it(\'should return false if the value is an object w/o a "then" function\', function () {\n      expect(utils.isPromise({}), \'to be\', false);\n    });\n\n    it(\'should return false if the object is null\', function () {\n      expect(utils.isPromise(null), \'to be\', false);\n    });\n  });\n\n  describe(\'escape()\', function () {\n    it(\'replaces the usual xml suspects\', function () {\n      expect(utils.escape(\'<a<bc<d<\'), \'to be\', \'&#x3C;a&#x3C;bc&#x3C;d&#x3C;\');\n      expect(utils.escape(\'>a>bc>d>\'), \'to be\', \'&#x3E;a&#x3E;bc&#x3E;d&#x3E;\');\n      expect(utils.escape(\'"a"bc"d"\'), \'to be\', \'&#x22;a&#x22;bc&#x22;d&#x22;\');\n      expect(utils.escape(\'<>"&\'), \'to be\', \'&#x3C;&#x3E;&#x22;&#x26;\');\n\n      expect(utils.escape(\'&a&bc&d&\'), \'to be\', \'&#x26;a&#x26;bc&#x26;d&#x26;\');\n      expect(utils.escape(\'&amp;&lt;\'), \'to be\', \'&#x26;amp;&#x26;lt;\');\n    });\n\n    it(\'replaces invalid xml characters\', function () {\n      expect(\n        utils.escape(\'\\x1B[32mfoo\\x1B[0m\'),\n        \'to be\',\n        \'&#x1B;[32mfoo&#x1B;[0m\'\n      );\n      // Ensure we can handle non-trivial unicode characters as well\n      expect(utils.escape(\'💩\'), \'to be\', \'&#x1F4A9;\');\n    });\n  });\n\n  describe(\'createMap()\', function () {\n    });\n  });\n\n  describe(\'sQuote()\', function() {\n    var str = \'xxx\';\n\n    it(\'should return its input as string wrapped in single quotes\', function() {\n      var expected = "\'xxx\'";\n      expect(utils.sQuote(str), \'to be\', expected);\n    });\n  });\n\n  describe(\'dQuote()\', function() {\n    var str = \'xxx\';\n\n    it(\'should return its input as string wrapped in double quotes\', function() {\n      var expected = \'"xxx"\';\n      expect(utils.dQuote(str), \'to be\', expected);\n    });\n  });\n\n  describe(\'createMap()\', function() {\n    it(\'should return an object with a null prototype\', function() {\n      expect(Object.getPrototypeOf(utils.createMap()), \'to be\', null);\n        foo: \'bar\'\n      });\n    });\n\n    it(\'should add props from all object parameters to the object\', function () {\n      expect(\n        utils.createMap({foo: \'bar\'}, {bar: \'baz\'}),\n        \'to exhaustively satisfy\',\n        {foo: \'bar\', bar: \'baz\'}\n      );\n    });\n  });\n\n  describe(\'slug()\', function () {\n    it(\'should convert the string to lowercase\', function () {\n      expect(utils.slug(\'FOO\'), \'to be\', \'foo\');\n    });\n\n    it(\'should convert whitespace to dashes\', function () {\n      expect(\n        utils.slug(\'peanut butter\\nand\\tjelly\'),\n        \'to be\',\n        \'peanut-butter-and-jelly\'\n      );\n    });\n\n    it(\'should strip non-alphanumeric and non-dash characters\', function () {\n      expect(utils.slug(\'murder-hornets!!\'), \'to be\', \'murder-hornets\');\n    });\n\n    it(\'should disallow consecutive dashes\', function () {\n      expect(utils.slug(\'poppies & fritz\'), \'to be\', \'poppies-fritz\');\n    });\n  });\n\n  describe(\'castArray()\', function () {\n    describe(\'when provided an array value\', function () {\n      it(\'should return a copy of the array\', function () {\n        const v = [\'foo\', \'bar\', \'baz\'];\n        expect(utils.castArray(v), \'to equal\', [\'foo\', \'bar\', \'baz\']).and(\n          \'not to be\',\n          v\n        );\n      });\n    });\n\n    describe(\'when provided an "arguments" value\', function () {\n      it(\'should return an array containing the arguments\', function () {\n        (function () {\n          expect(utils.castArray(arguments), \'to equal\', [\n            \'foo\',\n            \'bar\',\n            \'baz\'\n          ]).and(\'not to be\', arguments);\n        })(\'foo\', \'bar\', \'baz\');\n      });\n    });\n\n    describe(\'when provided an object\', function () {\n      it(\'should return an array containing the object only\', function () {\n        const v = {foo: \'bar\'};\n        expect(utils.castArray(v), \'to equal\', [v]);\n      });\n    });\n\n    describe(\'when provided no parameters\', function () {\n      it(\'should return an empty array\', function () {\n        expect(utils.castArray(), \'to equal\', []);\n      });\n    });\n\n    describe(\'when provided a primitive value\', function () {\n      it(\'should return an array containing the primitive value only\', function () {\n        expect(utils.castArray(\'butts\'), \'to equal\', [\'butts\']);\n      });\n    });\n\n    describe(\'when provided null\', function () {\n      it(\'should return an array containing a null value only\', function () {\n        expect(utils.castArray(null), \'to equal\', [null]);\n      });\n    });\n  });\n\n  describe(\'uniqueID()\', function () {\n    it(\'should return a non-empty string\', function () {\n      expect(utils.uniqueID(), \'to be a string\').and(\'not to be empty\');\n    });\n  });\n});\n', 'message': 'Add new option "--dry-run" (#4640)\n\n', 'author_name': 'GitHub', 'lang': 'spec'}