utils.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. var path = require('path');
  2. var fs = require('fs');
  3. var Util = module.exports = {};
  4. var Logger = require('./logger.js');
  5. var formatRegExp = /%[sdj%]/g;
  6. var testSuiteNameRegxp = /(_|-|\.)*([A-Z]*)/g;
  7. var nameSeparatorRegxp = /(\s|\/)/;
  8. Util.formatElapsedTime = function(timeMs, includeMs) {
  9. var seconds = timeMs/1000;
  10. return (seconds < 1 && timeMs + 'ms') ||
  11. (seconds > 1 && seconds < 60 && (seconds + 's')) ||
  12. (Math.floor(seconds/60) + 'm' + ' ' + Math.floor(seconds%60) + 's' + (includeMs ? (' / ' + timeMs + 'ms') : ''));
  13. };
  14. /**
  15. * Wrap a synchronous function, turning it into an async fn with a callback as
  16. * the last argument if necessary. `asyncArgCount` is the expected argument
  17. * count if `fn` is already asynchronous.
  18. *
  19. * @param {number} asyncArgCount
  20. * @param {function} fn
  21. * @param {object} [context]
  22. */
  23. Util.makeFnAsync = function (asyncArgCount, fn, context) {
  24. if (fn.length === asyncArgCount) {
  25. return fn;
  26. }
  27. return function() {
  28. var args = Array.prototype.slice.call(arguments, 0);
  29. var done = args.pop();
  30. context = context || null;
  31. fn.apply(context, args);
  32. done();
  33. };
  34. };
  35. /**
  36. * Waits a number of ms for a `done` callback to be invoked
  37. *
  38. * @param {function} done
  39. * @param {string} fnName
  40. * @param {number} timeMs
  41. * @param {function} [onCatch]
  42. * @param {function} [onTimerStarted]
  43. * @return {Function}
  44. */
  45. Util.setCallbackTimeout = function(done, fnName, timeMs, onCatch, onTimerStarted) {
  46. var timeout = setTimeout(function() {
  47. if (onCatch) {
  48. var err = new Error('done() callback timeout of '+ timeMs +' ms was reached while executing "' + fnName + '".' +
  49. ' Make sure to call the done() callback when the operation finishes.');
  50. onCatch(err, fnName, timeout);
  51. }
  52. }, timeMs);
  53. if (onTimerStarted) {
  54. onTimerStarted(timeout);
  55. }
  56. return function(err) {
  57. clearTimeout(timeout);
  58. done(err, true);
  59. };
  60. };
  61. Util.checkFunction = function(name, parent) {
  62. return parent && (typeof parent[name] == 'function') && parent[name] || false;
  63. };
  64. Util.getTestSuiteName = function(moduleName) {
  65. var words;
  66. moduleName = moduleName.replace(testSuiteNameRegxp, function(match, $0, $1, offset, string) {
  67. if (!match) {
  68. return '';
  69. }
  70. return (offset > 0 && (string.charAt(offset-1) !== ' ') ? ' ':'') + $1;
  71. });
  72. words = moduleName.split(nameSeparatorRegxp).map(function(word, index, matches) {
  73. if (word == '/') {
  74. return ' / ';
  75. }
  76. return word.charAt(0).toUpperCase() + word.substr(1);
  77. });
  78. return words.join('');
  79. };
  80. /**
  81. * A smaller version of util.format that doesn't support json and
  82. * if a placeholder is missing, it is omitted instead of appended
  83. *
  84. * @param f
  85. * @returns {string}
  86. */
  87. Util.format = function format(f) {
  88. var i = 1;
  89. var args = arguments;
  90. var len = args.length;
  91. return String(f).replace(formatRegExp, function(x) {
  92. if (x === '%%') {
  93. return '%';
  94. }
  95. if (i >= len) {
  96. return x;
  97. }
  98. switch (x) {
  99. case '%s':
  100. return String(args[i++]);
  101. case '%d':
  102. return Number(args[i++]);
  103. default:
  104. return x;
  105. }
  106. });
  107. };
  108. Util.getScreenshotFileName = function(currentTest, is_error, screenshots_path) {
  109. var prefix = currentTest.module + '/' + currentTest.name;
  110. prefix = prefix.replace(/\s/g, '-').replace(/"|'/g, '');
  111. prefix += is_error ? '_ERROR' : '_FAILED';
  112. var d = new Date();
  113. var dateParts = d.toString().replace(/:/g,'').split(' ');
  114. dateParts.shift();
  115. dateParts.pop();
  116. var dateStamp = dateParts.join('-');
  117. return path.resolve(path.join(screenshots_path, prefix + '_' + dateStamp + '.png'));
  118. };
  119. Util.isObject = function(obj) {
  120. return (typeof obj == 'object') && (obj !== null);
  121. };
  122. Util.processAsyncQueue = function(concurrency, files, cb) {
  123. var maxWorkers = Math.min(concurrency, files.length);
  124. var queue = [];
  125. var add = function(item) {
  126. queue.push(item);
  127. };
  128. var workers = 0;
  129. var index = 0;
  130. var next = function() {
  131. workers -= 1;
  132. process();
  133. };
  134. for (var i = 0; i < files.length; i++) {
  135. add(files[i]);
  136. }
  137. var process = function() {
  138. while (workers < maxWorkers) {
  139. workers += 1;
  140. if (queue.length) {
  141. var item = queue.shift();
  142. cb(item, index++, next);
  143. }
  144. }
  145. };
  146. process();
  147. };
  148. Util.getModuleKey = function(filePath, srcFolders, fullPaths) {
  149. var modulePathParts = filePath.split(path.sep);
  150. var diffInFolder = '';
  151. var folder = '';
  152. var parentFolder = '';
  153. var moduleName = modulePathParts.pop();
  154. filePath = modulePathParts.join(path.sep);
  155. if (srcFolders) {
  156. for (var i = 0; i < srcFolders.length; i++) {
  157. folder = path.resolve(srcFolders[i]);
  158. if (fullPaths.length > 1) {
  159. parentFolder = folder.split(path.sep).pop();
  160. }
  161. if (filePath.indexOf(folder) === 0) {
  162. diffInFolder = filePath.substring(folder.length + 1);
  163. break;
  164. }
  165. }
  166. }
  167. return path.join(parentFolder, diffInFolder, moduleName);
  168. };
  169. Util.showStackTraceWithHeadline = function(headline, stack, isErr) {
  170. var logMethod = isErr ? 'error' : 'log';
  171. var stackTrace;
  172. console[logMethod](Logger.colors.red(headline));
  173. if (Array.isArray(stack) && stack.length > 0) {
  174. stackTrace = Util.stackTraceFilter(stack);
  175. } else {
  176. stackTrace = stack;
  177. }
  178. if (stack) {
  179. console[logMethod](Logger.colors.stack_trace(stackTrace));
  180. }
  181. };
  182. Util.stackTraceFilter = function(parts) {
  183. var stack = parts.reduce(function(list, line) {
  184. if (contains(line, [
  185. 'node_modules',
  186. '(node.js:',
  187. '(events.js:'
  188. ])) {
  189. return list;
  190. }
  191. list.push(line);
  192. return list;
  193. }, []);
  194. return stack.join('\n');
  195. };
  196. var indentRegex = /^/gm;
  197. Util.showStackTrace = function(stack) {
  198. var parts = stack.split('\n');
  199. var headline = parts.shift();
  200. console.log(Logger.colors.red(headline.replace(indentRegex, ' ')));
  201. if (parts.length > 0) {
  202. var result = Util.stackTraceFilter(parts);
  203. console.log(Logger.colors.stack_trace(result.replace(indentRegex, ' ')));
  204. }
  205. };
  206. Util.symbols = (function() {
  207. var ok = String.fromCharCode(10004);
  208. var fail = String.fromCharCode(10006);
  209. if (process.platform === 'win32') {
  210. ok = '\u221A';
  211. fail = '\u00D7';
  212. }
  213. return {
  214. ok: ok,
  215. fail: fail
  216. };
  217. })();
  218. Util.isErrorObject = function(err) {
  219. return err instanceof Error || Object.prototype.toString.call(err) === '[object Error]';
  220. };
  221. // util to replace deprecated fs.existsSync
  222. Util.dirExistsSync = function (path) {
  223. try {
  224. return fs.statSync(path).isDirectory();
  225. } catch (e) {
  226. return false;
  227. }
  228. };
  229. Util.fileExistsSync = function (path) {
  230. try {
  231. return fs.statSync(path).isFile();
  232. } catch (e) {
  233. return false;
  234. }
  235. };
  236. function contains(str, text) {
  237. if (Array.isArray(text)) {
  238. for (var i = 0; i < text.length; i++) {
  239. if (contains(str, text[i])) {
  240. return true;
  241. }
  242. }
  243. }
  244. return str.indexOf(text) > -1;
  245. }