utils.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. 'use strict';
  2. /* eslint-env browser */
  3. /**
  4. * Module dependencies.
  5. */
  6. var JSON = require('json3');
  7. var basename = require('path').basename;
  8. var debug = require('debug')('mocha:watch');
  9. var exists = require('fs').existsSync || require('path').existsSync;
  10. var glob = require('glob');
  11. var path = require('path');
  12. var join = path.join;
  13. var readdirSync = require('fs').readdirSync;
  14. var statSync = require('fs').statSync;
  15. var watchFile = require('fs').watchFile;
  16. var lstatSync = require('fs').lstatSync;
  17. var toISOString = require('./to-iso-string');
  18. /**
  19. * Ignored directories.
  20. */
  21. var ignore = ['node_modules', '.git'];
  22. exports.inherits = require('util').inherits;
  23. /**
  24. * Escape special characters in the given string of html.
  25. *
  26. * @api private
  27. * @param {string} html
  28. * @return {string}
  29. */
  30. exports.escape = function (html) {
  31. return String(html)
  32. .replace(/&/g, '&')
  33. .replace(/"/g, '"')
  34. .replace(/</g, '&lt;')
  35. .replace(/>/g, '&gt;');
  36. };
  37. /**
  38. * Array#forEach (<=IE8)
  39. *
  40. * @api private
  41. * @param {Array} arr
  42. * @param {Function} fn
  43. * @param {Object} scope
  44. */
  45. exports.forEach = function (arr, fn, scope) {
  46. for (var i = 0, l = arr.length; i < l; i++) {
  47. fn.call(scope, arr[i], i);
  48. }
  49. };
  50. /**
  51. * Test if the given obj is type of string.
  52. *
  53. * @api private
  54. * @param {Object} obj
  55. * @return {boolean}
  56. */
  57. exports.isString = function (obj) {
  58. return typeof obj === 'string';
  59. };
  60. /**
  61. * Array#map (<=IE8)
  62. *
  63. * @api private
  64. * @param {Array} arr
  65. * @param {Function} fn
  66. * @param {Object} scope
  67. * @return {Array}
  68. */
  69. exports.map = function (arr, fn, scope) {
  70. var result = [];
  71. for (var i = 0, l = arr.length; i < l; i++) {
  72. result.push(fn.call(scope, arr[i], i, arr));
  73. }
  74. return result;
  75. };
  76. /**
  77. * Array#indexOf (<=IE8)
  78. *
  79. * @api private
  80. * @param {Array} arr
  81. * @param {Object} obj to find index of
  82. * @param {number} start
  83. * @return {number}
  84. */
  85. var indexOf = exports.indexOf = function (arr, obj, start) {
  86. for (var i = start || 0, l = arr.length; i < l; i++) {
  87. if (arr[i] === obj) {
  88. return i;
  89. }
  90. }
  91. return -1;
  92. };
  93. /**
  94. * Array#reduce (<=IE8)
  95. *
  96. * @api private
  97. * @param {Array} arr
  98. * @param {Function} fn
  99. * @param {Object} val Initial value.
  100. * @return {*}
  101. */
  102. var reduce = exports.reduce = function (arr, fn, val) {
  103. var rval = val;
  104. for (var i = 0, l = arr.length; i < l; i++) {
  105. rval = fn(rval, arr[i], i, arr);
  106. }
  107. return rval;
  108. };
  109. /**
  110. * Array#filter (<=IE8)
  111. *
  112. * @api private
  113. * @param {Array} arr
  114. * @param {Function} fn
  115. * @return {Array}
  116. */
  117. exports.filter = function (arr, fn) {
  118. var ret = [];
  119. for (var i = 0, l = arr.length; i < l; i++) {
  120. var val = arr[i];
  121. if (fn(val, i, arr)) {
  122. ret.push(val);
  123. }
  124. }
  125. return ret;
  126. };
  127. /**
  128. * Array#some (<=IE8)
  129. *
  130. * @api private
  131. * @param {Array} arr
  132. * @param {Function} fn
  133. * @return {Array}
  134. */
  135. exports.some = function (arr, fn) {
  136. for (var i = 0, l = arr.length; i < l; i++) {
  137. if (fn(arr[i])) {
  138. return true;
  139. }
  140. }
  141. return false;
  142. };
  143. /**
  144. * Object.keys (<=IE8)
  145. *
  146. * @api private
  147. * @param {Object} obj
  148. * @return {Array} keys
  149. */
  150. exports.keys = typeof Object.keys === 'function' ? Object.keys : function (obj) {
  151. var keys = [];
  152. var has = Object.prototype.hasOwnProperty; // for `window` on <=IE8
  153. for (var key in obj) {
  154. if (has.call(obj, key)) {
  155. keys.push(key);
  156. }
  157. }
  158. return keys;
  159. };
  160. /**
  161. * Watch the given `files` for changes
  162. * and invoke `fn(file)` on modification.
  163. *
  164. * @api private
  165. * @param {Array} files
  166. * @param {Function} fn
  167. */
  168. exports.watch = function (files, fn) {
  169. var options = { interval: 100 };
  170. files.forEach(function (file) {
  171. debug('file %s', file);
  172. watchFile(file, options, function (curr, prev) {
  173. if (prev.mtime < curr.mtime) {
  174. fn(file);
  175. }
  176. });
  177. });
  178. };
  179. /**
  180. * Array.isArray (<=IE8)
  181. *
  182. * @api private
  183. * @param {Object} obj
  184. * @return {Boolean}
  185. */
  186. var isArray = typeof Array.isArray === 'function' ? Array.isArray : function (obj) {
  187. return Object.prototype.toString.call(obj) === '[object Array]';
  188. };
  189. exports.isArray = isArray;
  190. /**
  191. * Buffer.prototype.toJSON polyfill.
  192. *
  193. * @type {Function}
  194. */
  195. if (typeof Buffer !== 'undefined' && Buffer.prototype) {
  196. Buffer.prototype.toJSON = Buffer.prototype.toJSON || function () {
  197. return Array.prototype.slice.call(this, 0);
  198. };
  199. }
  200. /**
  201. * Ignored files.
  202. *
  203. * @api private
  204. * @param {string} path
  205. * @return {boolean}
  206. */
  207. function ignored (path) {
  208. return !~ignore.indexOf(path);
  209. }
  210. /**
  211. * Lookup files in the given `dir`.
  212. *
  213. * @api private
  214. * @param {string} dir
  215. * @param {string[]} [ext=['.js']]
  216. * @param {Array} [ret=[]]
  217. * @return {Array}
  218. */
  219. exports.files = function (dir, ext, ret) {
  220. ret = ret || [];
  221. ext = ext || ['js'];
  222. var re = new RegExp('\\.(' + ext.join('|') + ')$');
  223. readdirSync(dir)
  224. .filter(ignored)
  225. .forEach(function (path) {
  226. path = join(dir, path);
  227. if (lstatSync(path).isDirectory()) {
  228. exports.files(path, ext, ret);
  229. } else if (path.match(re)) {
  230. ret.push(path);
  231. }
  232. });
  233. return ret;
  234. };
  235. /**
  236. * Compute a slug from the given `str`.
  237. *
  238. * @api private
  239. * @param {string} str
  240. * @return {string}
  241. */
  242. exports.slug = function (str) {
  243. return str
  244. .toLowerCase()
  245. .replace(/ +/g, '-')
  246. .replace(/[^-\w]/g, '');
  247. };
  248. /**
  249. * Strip the function definition from `str`, and re-indent for pre whitespace.
  250. *
  251. * @param {string} str
  252. * @return {string}
  253. */
  254. exports.clean = function (str) {
  255. str = str
  256. .replace(/\r\n?|[\n\u2028\u2029]/g, '\n').replace(/^\uFEFF/, '')
  257. // (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content
  258. .replace(/^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/, '$1$2$3');
  259. var spaces = str.match(/^\n?( *)/)[1].length;
  260. var tabs = str.match(/^\n?(\t*)/)[1].length;
  261. var re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs || spaces) + '}', 'gm');
  262. str = str.replace(re, '');
  263. return exports.trim(str);
  264. };
  265. /**
  266. * Trim the given `str`.
  267. *
  268. * @api private
  269. * @param {string} str
  270. * @return {string}
  271. */
  272. exports.trim = function (str) {
  273. return str.replace(/^\s+|\s+$/g, '');
  274. };
  275. /**
  276. * Parse the given `qs`.
  277. *
  278. * @api private
  279. * @param {string} qs
  280. * @return {Object}
  281. */
  282. exports.parseQuery = function (qs) {
  283. return reduce(qs.replace('?', '').split('&'), function (obj, pair) {
  284. var i = pair.indexOf('=');
  285. var key = pair.slice(0, i);
  286. var val = pair.slice(++i);
  287. obj[key] = decodeURIComponent(val);
  288. return obj;
  289. }, {});
  290. };
  291. /**
  292. * Highlight the given string of `js`.
  293. *
  294. * @api private
  295. * @param {string} js
  296. * @return {string}
  297. */
  298. function highlight (js) {
  299. return js
  300. .replace(/</g, '&lt;')
  301. .replace(/>/g, '&gt;')
  302. .replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
  303. .replace(/('.*?')/gm, '<span class="string">$1</span>')
  304. .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
  305. .replace(/(\d+)/gm, '<span class="number">$1</span>')
  306. .replace(/\bnew[ \t]+(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
  307. .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>');
  308. }
  309. /**
  310. * Highlight the contents of tag `name`.
  311. *
  312. * @api private
  313. * @param {string} name
  314. */
  315. exports.highlightTags = function (name) {
  316. var code = document.getElementById('mocha').getElementsByTagName(name);
  317. for (var i = 0, len = code.length; i < len; ++i) {
  318. code[i].innerHTML = highlight(code[i].innerHTML);
  319. }
  320. };
  321. /**
  322. * If a value could have properties, and has none, this function is called,
  323. * which returns a string representation of the empty value.
  324. *
  325. * Functions w/ no properties return `'[Function]'`
  326. * Arrays w/ length === 0 return `'[]'`
  327. * Objects w/ no properties return `'{}'`
  328. * All else: return result of `value.toString()`
  329. *
  330. * @api private
  331. * @param {*} value The value to inspect.
  332. * @param {string} typeHint The type of the value
  333. * @returns {string}
  334. */
  335. function emptyRepresentation (value, typeHint) {
  336. switch (typeHint) {
  337. case 'function':
  338. return '[Function]';
  339. case 'object':
  340. return '{}';
  341. case 'array':
  342. return '[]';
  343. default:
  344. return value.toString();
  345. }
  346. }
  347. /**
  348. * Takes some variable and asks `Object.prototype.toString()` what it thinks it
  349. * is.
  350. *
  351. * @api private
  352. * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
  353. * @param {*} value The value to test.
  354. * @returns {string} Computed type
  355. * @example
  356. * type({}) // 'object'
  357. * type([]) // 'array'
  358. * type(1) // 'number'
  359. * type(false) // 'boolean'
  360. * type(Infinity) // 'number'
  361. * type(null) // 'null'
  362. * type(new Date()) // 'date'
  363. * type(/foo/) // 'regexp'
  364. * type('type') // 'string'
  365. * type(global) // 'global'
  366. * type(new String('foo') // 'object'
  367. */
  368. var type = exports.type = function type (value) {
  369. if (value === undefined) {
  370. return 'undefined';
  371. } else if (value === null) {
  372. return 'null';
  373. } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
  374. return 'buffer';
  375. }
  376. return Object.prototype.toString.call(value)
  377. .replace(/^\[.+\s(.+?)\]$/, '$1')
  378. .toLowerCase();
  379. };
  380. /**
  381. * Stringify `value`. Different behavior depending on type of value:
  382. *
  383. * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
  384. * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
  385. * - If `value` is an *empty* object, function, or array, return result of function
  386. * {@link emptyRepresentation}.
  387. * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
  388. * JSON.stringify().
  389. *
  390. * @api private
  391. * @see exports.type
  392. * @param {*} value
  393. * @return {string}
  394. */
  395. exports.stringify = function (value) {
  396. var typeHint = type(value);
  397. if (!~indexOf(['object', 'array', 'function'], typeHint)) {
  398. if (typeHint === 'buffer') {
  399. var json = value.toJSON();
  400. // Based on the toJSON result
  401. return jsonStringify(json.data && json.type ? json.data : json, 2)
  402. .replace(/,(\n|$)/g, '$1');
  403. }
  404. // IE7/IE8 has a bizarre String constructor; needs to be coerced
  405. // into an array and back to obj.
  406. if (typeHint === 'string' && typeof value === 'object') {
  407. value = reduce(value.split(''), function (acc, char, idx) {
  408. acc[idx] = char;
  409. return acc;
  410. }, {});
  411. typeHint = 'object';
  412. } else {
  413. return jsonStringify(value);
  414. }
  415. }
  416. for (var prop in value) {
  417. if (Object.prototype.hasOwnProperty.call(value, prop)) {
  418. return jsonStringify(exports.canonicalize(value, null, typeHint), 2).replace(/,(\n|$)/g, '$1');
  419. }
  420. }
  421. return emptyRepresentation(value, typeHint);
  422. };
  423. /**
  424. * like JSON.stringify but more sense.
  425. *
  426. * @api private
  427. * @param {Object} object
  428. * @param {number=} spaces
  429. * @param {number=} depth
  430. * @returns {*}
  431. */
  432. function jsonStringify (object, spaces, depth) {
  433. if (typeof spaces === 'undefined') {
  434. // primitive types
  435. return _stringify(object);
  436. }
  437. depth = depth || 1;
  438. var space = spaces * depth;
  439. var str = isArray(object) ? '[' : '{';
  440. var end = isArray(object) ? ']' : '}';
  441. var length = typeof object.length === 'number' ? object.length : exports.keys(object).length;
  442. // `.repeat()` polyfill
  443. function repeat (s, n) {
  444. return new Array(n).join(s);
  445. }
  446. function _stringify (val) {
  447. switch (type(val)) {
  448. case 'null':
  449. case 'undefined':
  450. val = '[' + val + ']';
  451. break;
  452. case 'array':
  453. case 'object':
  454. val = jsonStringify(val, spaces, depth + 1);
  455. break;
  456. case 'boolean':
  457. case 'regexp':
  458. case 'symbol':
  459. case 'number':
  460. val = val === 0 && (1 / val) === -Infinity // `-0`
  461. ? '-0'
  462. : val.toString();
  463. break;
  464. case 'date':
  465. var sDate;
  466. if (isNaN(val.getTime())) { // Invalid date
  467. sDate = val.toString();
  468. } else {
  469. sDate = val.toISOString ? val.toISOString() : toISOString(val);
  470. }
  471. val = '[Date: ' + sDate + ']';
  472. break;
  473. case 'buffer':
  474. var json = val.toJSON();
  475. // Based on the toJSON result
  476. json = json.data && json.type ? json.data : json;
  477. val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']';
  478. break;
  479. default:
  480. val = (val === '[Function]' || val === '[Circular]')
  481. ? val
  482. : JSON.stringify(val); // string
  483. }
  484. return val;
  485. }
  486. for (var i in object) {
  487. if (!Object.prototype.hasOwnProperty.call(object, i)) {
  488. continue; // not my business
  489. }
  490. --length;
  491. str += '\n ' + repeat(' ', space) +
  492. (isArray(object) ? '' : '"' + i + '": ') + // key
  493. _stringify(object[i]) + // value
  494. (length ? ',' : ''); // comma
  495. }
  496. return str +
  497. // [], {}
  498. (str.length !== 1 ? '\n' + repeat(' ', --space) + end : end);
  499. }
  500. /**
  501. * Test if a value is a buffer.
  502. *
  503. * @api private
  504. * @param {*} value The value to test.
  505. * @return {boolean} True if `value` is a buffer, otherwise false
  506. */
  507. exports.isBuffer = function (value) {
  508. return typeof Buffer !== 'undefined' && Buffer.isBuffer(value);
  509. };
  510. /**
  511. * Return a new Thing that has the keys in sorted order. Recursive.
  512. *
  513. * If the Thing...
  514. * - has already been seen, return string `'[Circular]'`
  515. * - is `undefined`, return string `'[undefined]'`
  516. * - is `null`, return value `null`
  517. * - is some other primitive, return the value
  518. * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
  519. * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
  520. * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
  521. *
  522. * @api private
  523. * @see {@link exports.stringify}
  524. * @param {*} value Thing to inspect. May or may not have properties.
  525. * @param {Array} [stack=[]] Stack of seen values
  526. * @param {string} [typeHint] Type hint
  527. * @return {(Object|Array|Function|string|undefined)}
  528. */
  529. exports.canonicalize = function canonicalize (value, stack, typeHint) {
  530. var canonicalizedObj;
  531. /* eslint-disable no-unused-vars */
  532. var prop;
  533. /* eslint-enable no-unused-vars */
  534. typeHint = typeHint || type(value);
  535. function withStack (value, fn) {
  536. stack.push(value);
  537. fn();
  538. stack.pop();
  539. }
  540. stack = stack || [];
  541. if (indexOf(stack, value) !== -1) {
  542. return '[Circular]';
  543. }
  544. switch (typeHint) {
  545. case 'undefined':
  546. case 'buffer':
  547. case 'null':
  548. canonicalizedObj = value;
  549. break;
  550. case 'array':
  551. withStack(value, function () {
  552. canonicalizedObj = exports.map(value, function (item) {
  553. return exports.canonicalize(item, stack);
  554. });
  555. });
  556. break;
  557. case 'function':
  558. /* eslint-disable guard-for-in */
  559. for (prop in value) {
  560. canonicalizedObj = {};
  561. break;
  562. }
  563. /* eslint-enable guard-for-in */
  564. if (!canonicalizedObj) {
  565. canonicalizedObj = emptyRepresentation(value, typeHint);
  566. break;
  567. }
  568. /* falls through */
  569. case 'object':
  570. canonicalizedObj = canonicalizedObj || {};
  571. withStack(value, function () {
  572. exports.forEach(exports.keys(value).sort(), function (key) {
  573. canonicalizedObj[key] = exports.canonicalize(value[key], stack);
  574. });
  575. });
  576. break;
  577. case 'date':
  578. case 'number':
  579. case 'regexp':
  580. case 'boolean':
  581. case 'symbol':
  582. canonicalizedObj = value;
  583. break;
  584. default:
  585. canonicalizedObj = value + '';
  586. }
  587. return canonicalizedObj;
  588. };
  589. /**
  590. * Lookup file names at the given `path`.
  591. *
  592. * @api public
  593. * @param {string} path Base path to start searching from.
  594. * @param {string[]} extensions File extensions to look for.
  595. * @param {boolean} recursive Whether or not to recurse into subdirectories.
  596. * @return {string[]} An array of paths.
  597. */
  598. exports.lookupFiles = function lookupFiles (path, extensions, recursive) {
  599. var files = [];
  600. var re = new RegExp('\\.(' + extensions.join('|') + ')$');
  601. if (!exists(path)) {
  602. if (exists(path + '.js')) {
  603. path += '.js';
  604. } else {
  605. files = glob.sync(path);
  606. if (!files.length) {
  607. throw new Error("cannot resolve path (or pattern) '" + path + "'");
  608. }
  609. return files;
  610. }
  611. }
  612. try {
  613. var stat = statSync(path);
  614. if (stat.isFile()) {
  615. return path;
  616. }
  617. } catch (err) {
  618. // ignore error
  619. return;
  620. }
  621. readdirSync(path).forEach(function (file) {
  622. file = join(path, file);
  623. try {
  624. var stat = statSync(file);
  625. if (stat.isDirectory()) {
  626. if (recursive) {
  627. files = files.concat(lookupFiles(file, extensions, recursive));
  628. }
  629. return;
  630. }
  631. } catch (err) {
  632. // ignore error
  633. return;
  634. }
  635. if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') {
  636. return;
  637. }
  638. files.push(file);
  639. });
  640. return files;
  641. };
  642. /**
  643. * Generate an undefined error with a message warning the user.
  644. *
  645. * @return {Error}
  646. */
  647. exports.undefinedError = function () {
  648. return new Error('Caught undefined error, did you throw without specifying what?');
  649. };
  650. /**
  651. * Generate an undefined error if `err` is not defined.
  652. *
  653. * @param {Error} err
  654. * @return {Error}
  655. */
  656. exports.getError = function (err) {
  657. return err || exports.undefinedError();
  658. };
  659. /**
  660. * @summary
  661. * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
  662. * @description
  663. * When invoking this function you get a filter function that get the Error.stack as an input,
  664. * and return a prettify output.
  665. * (i.e: strip Mocha and internal node functions from stack trace).
  666. * @returns {Function}
  667. */
  668. exports.stackTraceFilter = function () {
  669. // TODO: Replace with `process.browser`
  670. var is = typeof document === 'undefined' ? { node: true } : { browser: true };
  671. var slash = path.sep;
  672. var cwd;
  673. if (is.node) {
  674. cwd = process.cwd() + slash;
  675. } else {
  676. cwd = (typeof location === 'undefined' ? window.location : location).href.replace(/\/[^\/]*$/, '/');
  677. slash = '/';
  678. }
  679. function isMochaInternal (line) {
  680. return (~line.indexOf('node_modules' + slash + 'mocha' + slash)) ||
  681. (~line.indexOf('node_modules' + slash + 'mocha.js')) ||
  682. (~line.indexOf('bower_components' + slash + 'mocha.js')) ||
  683. (~line.indexOf(slash + 'mocha.js'));
  684. }
  685. function isNodeInternal (line) {
  686. return (~line.indexOf('(timers.js:')) ||
  687. (~line.indexOf('(events.js:')) ||
  688. (~line.indexOf('(node.js:')) ||
  689. (~line.indexOf('(module.js:')) ||
  690. (~line.indexOf('GeneratorFunctionPrototype.next (native)')) ||
  691. false;
  692. }
  693. return function (stack) {
  694. stack = stack.split('\n');
  695. stack = reduce(stack, function (list, line) {
  696. if (isMochaInternal(line)) {
  697. return list;
  698. }
  699. if (is.node && isNodeInternal(line)) {
  700. return list;
  701. }
  702. // Clean up cwd(absolute)
  703. if (/\(?.+:\d+:\d+\)?$/.test(line)) {
  704. line = line.replace(cwd, '');
  705. }
  706. list.push(line);
  707. return list;
  708. }, []);
  709. return stack.join('\n');
  710. };
  711. };
  712. /**
  713. * Crude, but effective.
  714. * @api
  715. * @param {*} value
  716. * @returns {boolean} Whether or not `value` is a Promise
  717. */
  718. exports.isPromise = function isPromise (value) {
  719. return typeof value === 'object' && typeof value.then === 'function';
  720. };
  721. /**
  722. * It's a noop.
  723. * @api
  724. */
  725. exports.noop = function () {};