var path = require('path'); var fs = require('fs'); var Util = module.exports = {}; var Logger = require('./logger.js'); var formatRegExp = /%[sdj%]/g; var testSuiteNameRegxp = /(_|-|\.)*([A-Z]*)/g; var nameSeparatorRegxp = /(\s|\/)/; Util.formatElapsedTime = function(timeMs, includeMs) { var seconds = timeMs/1000; return (seconds < 1 && timeMs + 'ms') || (seconds > 1 && seconds < 60 && (seconds + 's')) || (Math.floor(seconds/60) + 'm' + ' ' + Math.floor(seconds%60) + 's' + (includeMs ? (' / ' + timeMs + 'ms') : '')); }; /** * Wrap a synchronous function, turning it into an async fn with a callback as * the last argument if necessary. `asyncArgCount` is the expected argument * count if `fn` is already asynchronous. * * @param {number} asyncArgCount * @param {function} fn * @param {object} [context] */ Util.makeFnAsync = function (asyncArgCount, fn, context) { if (fn.length === asyncArgCount) { return fn; } return function() { var args = Array.prototype.slice.call(arguments, 0); var done = args.pop(); context = context || null; fn.apply(context, args); done(); }; }; /** * Waits a number of ms for a `done` callback to be invoked * * @param {function} done * @param {string} fnName * @param {number} timeMs * @param {function} [onCatch] * @param {function} [onTimerStarted] * @return {Function} */ Util.setCallbackTimeout = function(done, fnName, timeMs, onCatch, onTimerStarted) { var timeout = setTimeout(function() { if (onCatch) { var err = new Error('done() callback timeout of '+ timeMs +' ms was reached while executing "' + fnName + '".' + ' Make sure to call the done() callback when the operation finishes.'); onCatch(err, fnName, timeout); } }, timeMs); if (onTimerStarted) { onTimerStarted(timeout); } return function(ex) { clearTimeout(timeout); done(ex, true); }; }; Util.checkFunction = function(name, parent) { return parent && (typeof parent[name] == 'function') && parent[name] || false; }; Util.getTestSuiteName = function(moduleName) { var words; moduleName = moduleName.replace(testSuiteNameRegxp, function(match, $0, $1, offset, string) { if (!match) { return ''; } return (offset > 0 && (string.charAt(offset-1) !== ' ') ? ' ':'') + $1; }); words = moduleName.split(nameSeparatorRegxp).map(function(word, index, matches) { if (word == '/') { return ' / '; } return word.charAt(0).toUpperCase() + word.substr(1); }); return words.join(''); }; /** * A smaller version of util.format that doesn't support json and * if a placeholder is missing, it is omitted instead of appended * * @param f * @returns {string} */ Util.format = function format(f) { var i = 1; var args = arguments; var len = args.length; return String(f).replace(formatRegExp, function(x) { if (x === '%%') { return '%'; } if (i >= len) { return x; } switch (x) { case '%s': return String(args[i++]); case '%d': return Number(args[i++]); default: return x; } }); }; Util.getScreenshotFileName = function(currentTest, is_error, screenshots_path) { var prefix = currentTest.module + '/' + currentTest.name; prefix = prefix.replace(/\s/g, '-').replace(/"|'/g, ''); prefix += is_error ? '_ERROR' : '_FAILED'; var d = new Date(); var dateParts = d.toString().replace(/:/g,'').split(' '); dateParts.shift(); dateParts.pop(); var dateStamp = dateParts.join('-'); return path.resolve(path.join(screenshots_path, prefix + '_' + dateStamp + '.png')); }; Util.isObject = function(obj) { return (typeof obj == 'object') && (obj !== null); }; Util.processAsyncQueue = function(concurrency, files, cb) { var maxWorkers = Math.min(concurrency, files.length); var queue = []; var add = function(item) { queue.push(item); }; var workers = 0; var index = 0; var next = function() { workers -= 1; process(); }; for (var i = 0; i < files.length; i++) { add(files[i]); } var process = function() { while (workers < maxWorkers) { workers += 1; if (queue.length) { var item = queue.shift(); cb(item, index++, next); } } }; process(); }; Util.getModuleKey = function(filePath, srcFolders, fullPaths) { var modulePathParts = filePath.split(path.sep); var diffInFolder = ''; var folder = ''; var parentFolder = ''; var moduleName = modulePathParts.pop(); filePath = modulePathParts.join(path.sep); if (srcFolders) { for (var i = 0; i < srcFolders.length; i++) { folder = path.resolve(srcFolders[i]); if (fullPaths.length > 1) { parentFolder = folder.split(path.sep).pop(); } if (filePath.indexOf(folder) === 0) { diffInFolder = filePath.substring(folder.length + 1); break; } } } return path.join(parentFolder, diffInFolder, moduleName); }; Util.showStackTraceWithHeadline = function(headline, stack, isErr) { var logMethod = isErr ? 'error' : 'log'; var stackTrace; console[logMethod](Logger.colors.red(headline)); if (Array.isArray(stack) && stack.length > 0) { stackTrace = Util.stackTraceFilter(stack); } else { stackTrace = stack; } if (stack) { console[logMethod](Logger.colors.stack_trace(stackTrace)); } }; Util.stackTraceFilter = function(parts) { var stack = parts.reduce(function(list, line) { if (contains(line, [ 'node_modules', '(node.js:', '(events.js:' ])) { return list; } list.push(line); return list; }, []); return stack.join('\n'); }; var indentRegex = /^/gm; Util.showStackTrace = function(stack) { var parts = stack.split('\n'); var headline = parts.shift(); console.log(Logger.colors.red(headline.replace(indentRegex, ' '))); if (parts.length > 0) { var result = Util.stackTraceFilter(parts); console.log(Logger.colors.stack_trace(result.replace(indentRegex, ' '))); } }; Util.symbols = (function() { var ok = String.fromCharCode(10004); var fail = String.fromCharCode(10006); if (process.platform === 'win32') { ok = '\u221A'; fail = '\u00D7'; } return { ok: ok, fail: fail }; })(); Util.isErrorObject = function(err) { return err instanceof Error || Object.prototype.toString.call(err) === '[object Error]'; }; // util to replace deprecated fs.existsSync Util.dirExistsSync = function (path) { try { return fs.statSync(path).isDirectory(); } catch (e) { return false; } }; Util.fileExistsSync = function (path) { try { return fs.statSync(path).isFile(); } catch (e) { return false; } }; function contains(str, text) { if (Array.isArray(text)) { for (var i = 0; i < text.length; i++) { if (contains(str, text[i])) { return true; } } } return str.indexOf(text) > -1; }