mocha 6.0.0-1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/utils.js CHANGED
@@ -1,31 +1,35 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * @module
4
+ * Various utility functions used throughout Mocha's codebase.
5
+ * @module utils
5
6
  */
6
7
 
7
8
  /**
8
9
  * Module dependencies.
9
10
  */
10
11
 
11
- var debug = require('debug')('mocha:watch');
12
12
  var fs = require('fs');
13
- var glob = require('glob');
14
13
  var path = require('path');
15
- var join = path.join;
14
+ var util = require('util');
15
+ var glob = require('glob');
16
16
  var he = require('he');
17
17
  var errors = require('./errors');
18
18
  var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError;
19
19
  var createMissingArgumentError = errors.createMissingArgumentError;
20
- var createUndefinedError = errors.createUndefinedError;
20
+
21
+ var assign = (exports.assign = require('object.assign').getPolyfill());
21
22
 
22
23
  /**
23
- * Ignored directories.
24
+ * Inherit the prototype methods from one constructor into another.
25
+ *
26
+ * @param {function} ctor - Constructor function which needs to inherit the
27
+ * prototype.
28
+ * @param {function} superCtor - Constructor function to inherit prototype from.
29
+ * @throws {TypeError} if either constructor is null, or if super constructor
30
+ * lacks a prototype.
24
31
  */
25
-
26
- var ignore = ['node_modules', '.git'];
27
-
28
- exports.inherits = require('util').inherits;
32
+ exports.inherits = util.inherits;
29
33
 
30
34
  /**
31
35
  * Escape special characters in the given string of html.
@@ -59,6 +63,7 @@ exports.isString = function(obj) {
59
63
  */
60
64
  exports.watch = function(files, fn) {
61
65
  var options = {interval: 100};
66
+ var debug = require('debug')('mocha:watch');
62
67
  files.forEach(function(file) {
63
68
  debug('file %s', file);
64
69
  fs.watchFile(file, options, function(curr, prev) {
@@ -70,39 +75,52 @@ exports.watch = function(files, fn) {
70
75
  };
71
76
 
72
77
  /**
73
- * Ignored files.
78
+ * Predicate to screen `pathname` for further consideration.
79
+ *
80
+ * @description
81
+ * Returns <code>false</code> for pathname referencing:
82
+ * <ul>
83
+ * <li>'npm' package installation directory
84
+ * <li>'git' version control directory
85
+ * </ul>
74
86
  *
75
87
  * @private
76
- * @param {string} path
77
- * @return {boolean}
88
+ * @param {string} pathname - File or directory name to screen
89
+ * @return {boolean} whether pathname should be further considered
90
+ * @example
91
+ * ['node_modules', 'test.js'].filter(considerFurther); // => ['test.js']
78
92
  */
79
- function ignored(path) {
80
- return !~ignore.indexOf(path);
93
+ function considerFurther(pathname) {
94
+ var ignore = ['node_modules', '.git'];
95
+
96
+ return !~ignore.indexOf(pathname);
81
97
  }
82
98
 
83
99
  /**
84
100
  * Lookup files in the given `dir`.
85
101
  *
102
+ * @description
103
+ * Filenames are returned in _traversal_ order by the OS/filesystem.
104
+ * **Make no assumption that the names will be sorted in any fashion.**
105
+ *
86
106
  * @private
87
107
  * @param {string} dir
88
- * @param {string[]} [ext=['.js']]
108
+ * @param {string[]} [exts=['js']]
89
109
  * @param {Array} [ret=[]]
90
110
  * @return {Array}
91
111
  */
92
- exports.files = function(dir, ext, ret) {
112
+ exports.files = function(dir, exts, ret) {
93
113
  ret = ret || [];
94
- ext = ext || ['js'];
95
-
96
- var re = new RegExp('\\.(' + ext.join('|') + ')$');
114
+ exts = exts || ['js'];
97
115
 
98
116
  fs.readdirSync(dir)
99
- .filter(ignored)
100
- .forEach(function(path) {
101
- path = join(dir, path);
102
- if (fs.lstatSync(path).isDirectory()) {
103
- exports.files(path, ext, ret);
104
- } else if (path.match(re)) {
105
- ret.push(path);
117
+ .filter(considerFurther)
118
+ .forEach(function(dirent) {
119
+ var pathname = path.join(dir, dirent);
120
+ if (fs.lstatSync(pathname).isDirectory()) {
121
+ exports.files(pathname, exts, ret);
122
+ } else if (hasMatchingExtname(pathname, exts)) {
123
+ ret.push(pathname);
106
124
  }
107
125
  });
108
126
 
@@ -499,28 +517,72 @@ exports.canonicalize = function canonicalize(value, stack, typeHint) {
499
517
  return canonicalizedObj;
500
518
  };
501
519
 
520
+ /**
521
+ * Determines if pathname has a matching file extension.
522
+ *
523
+ * @private
524
+ * @param {string} pathname - Pathname to check for match.
525
+ * @param {string[]} exts - List of file extensions (sans period).
526
+ * @return {boolean} whether file extension matches.
527
+ * @example
528
+ * hasMatchingExtname('foo.html', ['js', 'css']); // => false
529
+ */
530
+ function hasMatchingExtname(pathname, exts) {
531
+ var suffix = path.extname(pathname).slice(1);
532
+ return exts.some(function(element) {
533
+ return suffix === element;
534
+ });
535
+ }
536
+
537
+ /**
538
+ * Determines if pathname would be a "hidden" file (or directory) on UN*X.
539
+ *
540
+ * @description
541
+ * On UN*X, pathnames beginning with a full stop (aka dot) are hidden during
542
+ * typical usage. Dotfiles, plain-text configuration files, are prime examples.
543
+ *
544
+ * @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names}
545
+ *
546
+ * @private
547
+ * @param {string} pathname - Pathname to check for match.
548
+ * @return {boolean} whether pathname would be considered a hidden file.
549
+ * @example
550
+ * isHiddenOnUnix('.profile'); // => true
551
+ */
552
+ function isHiddenOnUnix(pathname) {
553
+ return path.basename(pathname)[0] === '.';
554
+ }
555
+
502
556
  /**
503
557
  * Lookup file names at the given `path`.
504
558
  *
505
- * @memberof Mocha.utils
559
+ * @description
560
+ * Filenames are returned in _traversal_ order by the OS/filesystem.
561
+ * **Make no assumption that the names will be sorted in any fashion.**
562
+ *
506
563
  * @public
507
- * @param {string} filepath Base path to start searching from.
508
- * @param {string[]} extensions File extensions to look for.
509
- * @param {boolean} recursive Whether or not to recurse into subdirectories.
564
+ * @memberof Mocha.utils
510
565
  * @todo Fix extension handling
566
+ * @param {string} filepath - Base path to start searching from.
567
+ * @param {string[]} extensions - File extensions to look for.
568
+ * @param {boolean} recursive - Whether to recurse into subdirectories.
511
569
  * @return {string[]} An array of paths.
570
+ * @throws {Error} if no files match pattern.
571
+ * @throws {TypeError} if `filepath` is directory and `extensions` not provided.
512
572
  */
513
573
  exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
514
574
  var files = [];
575
+ var stat;
515
576
 
516
577
  if (!fs.existsSync(filepath)) {
517
578
  if (fs.existsSync(filepath + '.js')) {
518
579
  filepath += '.js';
519
580
  } else {
581
+ // Handle glob
520
582
  files = glob.sync(filepath);
521
583
  if (!files.length) {
522
584
  throw createNoFilesMatchPatternError(
523
- 'cannot find any files matching pattern "' + filepath + '"',
585
+ 'Cannot find any files matching pattern ' + exports.dQuote(filepath),
524
586
  filepath
525
587
  );
526
588
  }
@@ -528,8 +590,9 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
528
590
  }
529
591
  }
530
592
 
593
+ // Handle file
531
594
  try {
532
- var stat = fs.statSync(filepath);
595
+ stat = fs.statSync(filepath);
533
596
  if (stat.isFile()) {
534
597
  return filepath;
535
598
  }
@@ -538,13 +601,16 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
538
601
  return;
539
602
  }
540
603
 
541
- fs.readdirSync(filepath).forEach(function(file) {
542
- file = path.join(filepath, file);
604
+ // Handle directory
605
+ fs.readdirSync(filepath).forEach(function(dirent) {
606
+ var pathname = path.join(filepath, dirent);
607
+ var stat;
608
+
543
609
  try {
544
- var stat = fs.statSync(file);
610
+ stat = fs.statSync(pathname);
545
611
  if (stat.isDirectory()) {
546
612
  if (recursive) {
547
- files = files.concat(lookupFiles(file, extensions, recursive));
613
+ files = files.concat(lookupFiles(pathname, extensions, recursive));
548
614
  }
549
615
  return;
550
616
  }
@@ -554,64 +620,73 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
554
620
  }
555
621
  if (!extensions) {
556
622
  throw createMissingArgumentError(
557
- 'Argument "extensions" required when argument "filepath" is a directory',
623
+ util.format(
624
+ 'Argument %s required when argument %s is a directory',
625
+ exports.sQuote('extensions'),
626
+ exports.sQuote('filepath')
627
+ ),
558
628
  'extensions',
559
629
  'array'
560
630
  );
561
631
  }
562
- var re = new RegExp('\\.(?:' + extensions.join('|') + ')$');
563
- if (!stat.isFile() || !re.test(file) || path.basename(file)[0] === '.') {
632
+
633
+ if (
634
+ !stat.isFile() ||
635
+ !hasMatchingExtname(pathname, extensions) ||
636
+ isHiddenOnUnix(pathname)
637
+ ) {
564
638
  return;
565
639
  }
566
- files.push(file);
640
+ files.push(pathname);
567
641
  });
568
642
 
569
643
  return files;
570
644
  };
571
645
 
572
646
  /**
573
- * Generate an undefined error with a message warning the user.
574
- *
575
- * @return {Error}
647
+ * process.emitWarning or a polyfill
648
+ * @see https://nodejs.org/api/process.html#process_process_emitwarning_warning_options
649
+ * @ignore
576
650
  */
577
-
578
- exports.undefinedError = function() {
579
- return createUndefinedError(
580
- 'Caught undefined error, did you throw without specifying what?'
581
- );
582
- };
583
-
584
- /**
585
- * Generate an undefined error if `err` is not defined.
586
- *
587
- * @param {Error} err
588
- * @return {Error}
589
- */
590
-
591
- exports.getError = function(err) {
592
- return err || exports.undefinedError();
593
- };
651
+ function emitWarning(msg, type) {
652
+ if (process.emitWarning) {
653
+ process.emitWarning(msg, type);
654
+ } else {
655
+ process.nextTick(function() {
656
+ console.warn(type + ': ' + msg);
657
+ });
658
+ }
659
+ }
594
660
 
595
661
  /**
596
662
  * Show a deprecation warning. Each distinct message is only displayed once.
663
+ * Ignores empty messages.
597
664
  *
598
- * @param {string} msg
665
+ * @param {string} [msg] - Warning to print
666
+ * @private
599
667
  */
600
668
  exports.deprecate = function deprecate(msg) {
601
669
  msg = String(msg);
602
670
  if (msg && !deprecate.cache[msg]) {
603
671
  deprecate.cache[msg] = true;
604
- if (process.emitWarning) {
605
- process.emitWarning(msg, 'DeprecationWarning');
606
- } else {
607
- process.nextTick(function() {
608
- console.warn(msg);
609
- });
610
- }
672
+ emitWarning(msg, 'DeprecationWarning');
611
673
  }
612
674
  };
613
675
  exports.deprecate.cache = {};
614
676
 
677
+ /**
678
+ * Show a generic warning.
679
+ * Ignores empty messages.
680
+ *
681
+ * @param {string} [msg] - Warning to print
682
+ * @private
683
+ */
684
+ exports.warn = function warn(msg) {
685
+ if (msg) {
686
+ emitWarning(msg);
687
+ }
688
+ };
689
+
615
690
  /**
616
691
  * @summary
617
692
  * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
@@ -639,8 +714,6 @@ exports.stackTraceFilter = function() {
639
714
  function isMochaInternal(line) {
640
715
  return (
641
716
  ~line.indexOf('node_modules' + slash + 'mocha' + slash) ||
642
- ~line.indexOf('node_modules' + slash + 'mocha.js') ||
643
- ~line.indexOf('bower_components' + slash + 'mocha.js') ||
644
717
  ~line.indexOf(slash + 'mocha.js')
645
718
  );
646
719
  }
@@ -669,7 +742,7 @@ exports.stackTraceFilter = function() {
669
742
  }
670
743
 
671
744
  // Clean up cwd(absolute)
672
- if (/\(?.+:\d+:\d+\)?$/.test(line)) {
745
+ if (/:\d+:\d+\)?$/.test(line)) {
673
746
  line = line.replace('(' + cwd, '(');
674
747
  }
675
748
 
@@ -706,8 +779,119 @@ exports.clamp = function clamp(value, range) {
706
779
  return Math.min(Math.max(value, range[0]), range[1]);
707
780
  };
708
781
 
782
+ /**
783
+ * Single quote text by combining with undirectional ASCII quotation marks.
784
+ *
785
+ * @description
786
+ * Provides a simple means of markup for quoting text to be used in output.
787
+ * Use this to quote names of variables, methods, and packages.
788
+ *
789
+ * <samp>package 'foo' cannot be found</samp>
790
+ *
791
+ * @private
792
+ * @param {string} str - Value to be quoted.
793
+ * @returns {string} quoted value
794
+ * @example
795
+ * sQuote('n') // => 'n'
796
+ */
797
+ exports.sQuote = function(str) {
798
+ return "'" + str + "'";
799
+ };
800
+
801
+ /**
802
+ * Double quote text by combining with undirectional ASCII quotation marks.
803
+ *
804
+ * @description
805
+ * Provides a simple means of markup for quoting text to be used in output.
806
+ * Use this to quote names of datatypes, classes, pathnames, and strings.
807
+ *
808
+ * <samp>argument 'value' must be "string" or "number"</samp>
809
+ *
810
+ * @private
811
+ * @param {string} str - Value to be quoted.
812
+ * @returns {string} quoted value
813
+ * @example
814
+ * dQuote('number') // => "number"
815
+ */
816
+ exports.dQuote = function(str) {
817
+ return '"' + str + '"';
818
+ };
819
+
820
+ /**
821
+ * Provides simplistic message translation for dealing with plurality.
822
+ *
823
+ * @description
824
+ * Use this to create messages which need to be singular or plural.
825
+ * Some languages have several plural forms, so _complete_ message clauses
826
+ * are preferable to generating the message on the fly.
827
+ *
828
+ * @private
829
+ * @param {number} n - Non-negative integer
830
+ * @param {string} msg1 - Message to be used in English for `n = 1`
831
+ * @param {string} msg2 - Message to be used in English for `n = 0, 2, 3, ...`
832
+ * @returns {string} message corresponding to value of `n`
833
+ * @example
834
+ * var sprintf = require('util').format;
835
+ * var pkgs = ['one', 'two'];
836
+ * var msg = sprintf(
837
+ * ngettext(
838
+ * pkgs.length,
839
+ * 'cannot load package: %s',
840
+ * 'cannot load packages: %s'
841
+ * ),
842
+ * pkgs.map(sQuote).join(', ')
843
+ * );
844
+ * console.log(msg); // => cannot load packages: 'one', 'two'
845
+ */
846
+ exports.ngettext = function(n, msg1, msg2) {
847
+ if (typeof n === 'number' && n >= 0) {
848
+ return n === 1 ? msg1 : msg2;
849
+ }
850
+ };
851
+
709
852
  /**
710
853
  * It's a noop.
711
854
  * @public
712
855
  */
713
856
  exports.noop = function() {};
857
+
858
+ /**
859
+ * Creates a map-like object.
860
+ *
861
+ * @description
862
+ * A "map" is an object with no prototype, for our purposes. In some cases
863
+ * this would be more appropriate than a `Map`, especially if your environment
864
+ * doesn't support it. Recommended for use in Mocha's public APIs.
865
+ *
866
+ * @public
867
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map|MDN:Map}
868
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects|MDN:Object.create - Custom objects}
869
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign|MDN:Object.assign}
870
+ * @param {...*} [obj] - Arguments to `Object.assign()`.
871
+ * @returns {Object} An object with no prototype, having `...obj` properties
872
+ */
873
+ exports.createMap = function(obj) {
874
+ return assign.apply(
875
+ null,
876
+ [Object.create(null)].concat(Array.prototype.slice.call(arguments))
877
+ );
878
+ };
879
+
880
+ /**
881
+ * Creates a read-only map-like object.
882
+ *
883
+ * @description
884
+ * This differs from {@link module:utils.createMap createMap} only in that
885
+ * the argument must be non-empty, because the result is frozen.
886
+ *
887
+ * @see {@link module:utils.createMap createMap}
888
+ * @param {...*} [obj] - Arguments to `Object.assign()`.
889
+ * @returns {Object} A frozen object with no prototype, having `...obj` properties
890
+ * @throws {TypeError} if argument is not a non-empty object.
891
+ */
892
+ exports.defineConstants = function(obj) {
893
+ if (type(obj) !== 'object' || !Object.keys(obj).length) {
894
+ throw new TypeError('Invalid argument; expected a non-empty object');
895
+ }
896
+ return Object.freeze(exports.createMap(obj));
897
+ };