utils.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.wrapAnsiString = exports.getSummary = exports.relativePath = exports.formatTestPath = exports.trimAndFormatPath = exports.printDisplayName = void 0;
  6. function _path() {
  7. const data = _interopRequireDefault(require('path'));
  8. _path = function _path() {
  9. return data;
  10. };
  11. return data;
  12. }
  13. function _chalk() {
  14. const data = _interopRequireDefault(require('chalk'));
  15. _chalk = function _chalk() {
  16. return data;
  17. };
  18. return data;
  19. }
  20. function _slash() {
  21. const data = _interopRequireDefault(require('slash'));
  22. _slash = function _slash() {
  23. return data;
  24. };
  25. return data;
  26. }
  27. function _jestUtil() {
  28. const data = require('jest-util');
  29. _jestUtil = function _jestUtil() {
  30. return data;
  31. };
  32. return data;
  33. }
  34. function _interopRequireDefault(obj) {
  35. return obj && obj.__esModule ? obj : {default: obj};
  36. }
  37. /**
  38. * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  39. *
  40. * This source code is licensed under the MIT license found in the
  41. * LICENSE file in the root directory of this source tree.
  42. */
  43. const PROGRESS_BAR_WIDTH = 40;
  44. const printDisplayName = config => {
  45. const displayName = config.displayName;
  46. const white = _chalk().default.reset.inverse.white;
  47. if (!displayName) {
  48. return '';
  49. }
  50. if (typeof displayName === 'string') {
  51. return _chalk().default.supportsColor
  52. ? white(` ${displayName} `)
  53. : displayName;
  54. }
  55. const name = displayName.name,
  56. color = displayName.color;
  57. const chosenColor = _chalk().default.reset.inverse[color]
  58. ? _chalk().default.reset.inverse[color]
  59. : white;
  60. return _chalk().default.supportsColor ? chosenColor(` ${name} `) : name;
  61. };
  62. exports.printDisplayName = printDisplayName;
  63. const trimAndFormatPath = (pad, config, testPath, columns) => {
  64. const maxLength = columns - pad;
  65. const relative = relativePath(config, testPath);
  66. const basename = relative.basename;
  67. let dirname = relative.dirname; // length is ok
  68. if ((dirname + _path().default.sep + basename).length <= maxLength) {
  69. return (0, _slash().default)(
  70. _chalk().default.dim(dirname + _path().default.sep) +
  71. _chalk().default.bold(basename)
  72. );
  73. } // we can fit trimmed dirname and full basename
  74. const basenameLength = basename.length;
  75. if (basenameLength + 4 < maxLength) {
  76. const dirnameLength = maxLength - 4 - basenameLength;
  77. dirname =
  78. '...' + dirname.slice(dirname.length - dirnameLength, dirname.length);
  79. return (0, _slash().default)(
  80. _chalk().default.dim(dirname + _path().default.sep) +
  81. _chalk().default.bold(basename)
  82. );
  83. }
  84. if (basenameLength + 4 === maxLength) {
  85. return (0, _slash().default)(
  86. _chalk().default.dim('...' + _path().default.sep) +
  87. _chalk().default.bold(basename)
  88. );
  89. } // can't fit dirname, but can fit trimmed basename
  90. return (0, _slash().default)(
  91. _chalk().default.bold(
  92. '...' + basename.slice(basename.length - maxLength - 4, basename.length)
  93. )
  94. );
  95. };
  96. exports.trimAndFormatPath = trimAndFormatPath;
  97. const formatTestPath = (config, testPath) => {
  98. const _relativePath = relativePath(config, testPath),
  99. dirname = _relativePath.dirname,
  100. basename = _relativePath.basename;
  101. return (0, _slash().default)(
  102. _chalk().default.dim(dirname + _path().default.sep) +
  103. _chalk().default.bold(basename)
  104. );
  105. };
  106. exports.formatTestPath = formatTestPath;
  107. const relativePath = (config, testPath) => {
  108. // this function can be called with ProjectConfigs or GlobalConfigs. GlobalConfigs
  109. // do not have config.cwd, only config.rootDir. Try using config.cwd, fallback
  110. // to config.rootDir. (Also, some unit just use config.rootDir, which is ok)
  111. testPath = _path().default.relative(config.cwd || config.rootDir, testPath);
  112. const dirname = _path().default.dirname(testPath);
  113. const basename = _path().default.basename(testPath);
  114. return {
  115. basename,
  116. dirname
  117. };
  118. };
  119. exports.relativePath = relativePath;
  120. const getSummary = (aggregatedResults, options) => {
  121. let runTime = (Date.now() - aggregatedResults.startTime) / 1000;
  122. if (options && options.roundTime) {
  123. runTime = Math.floor(runTime);
  124. }
  125. const estimatedTime = (options && options.estimatedTime) || 0;
  126. const snapshotResults = aggregatedResults.snapshot;
  127. const snapshotsAdded = snapshotResults.added;
  128. const snapshotsFailed = snapshotResults.unmatched;
  129. const snapshotsOutdated = snapshotResults.unchecked;
  130. const snapshotsFilesRemoved = snapshotResults.filesRemoved;
  131. const snapshotsDidUpdate = snapshotResults.didUpdate;
  132. const snapshotsPassed = snapshotResults.matched;
  133. const snapshotsTotal = snapshotResults.total;
  134. const snapshotsUpdated = snapshotResults.updated;
  135. const suitesFailed = aggregatedResults.numFailedTestSuites;
  136. const suitesPassed = aggregatedResults.numPassedTestSuites;
  137. const suitesPending = aggregatedResults.numPendingTestSuites;
  138. const suitesRun = suitesFailed + suitesPassed;
  139. const suitesTotal = aggregatedResults.numTotalTestSuites;
  140. const testsFailed = aggregatedResults.numFailedTests;
  141. const testsPassed = aggregatedResults.numPassedTests;
  142. const testsPending = aggregatedResults.numPendingTests;
  143. const testsTodo = aggregatedResults.numTodoTests;
  144. const testsTotal = aggregatedResults.numTotalTests;
  145. const width = (options && options.width) || 0;
  146. const suites =
  147. _chalk().default.bold('Test Suites: ') +
  148. (suitesFailed
  149. ? _chalk().default.bold.red(`${suitesFailed} failed`) + ', '
  150. : '') +
  151. (suitesPending
  152. ? _chalk().default.bold.yellow(`${suitesPending} skipped`) + ', '
  153. : '') +
  154. (suitesPassed
  155. ? _chalk().default.bold.green(`${suitesPassed} passed`) + ', '
  156. : '') +
  157. (suitesRun !== suitesTotal
  158. ? suitesRun + ' of ' + suitesTotal
  159. : suitesTotal) +
  160. ` total`;
  161. const tests =
  162. _chalk().default.bold('Tests: ') +
  163. (testsFailed
  164. ? _chalk().default.bold.red(`${testsFailed} failed`) + ', '
  165. : '') +
  166. (testsPending
  167. ? _chalk().default.bold.yellow(`${testsPending} skipped`) + ', '
  168. : '') +
  169. (testsTodo
  170. ? _chalk().default.bold.magenta(`${testsTodo} todo`) + ', '
  171. : '') +
  172. (testsPassed
  173. ? _chalk().default.bold.green(`${testsPassed} passed`) + ', '
  174. : '') +
  175. `${testsTotal} total`;
  176. const snapshots =
  177. _chalk().default.bold('Snapshots: ') +
  178. (snapshotsFailed
  179. ? _chalk().default.bold.red(`${snapshotsFailed} failed`) + ', '
  180. : '') +
  181. (snapshotsOutdated && !snapshotsDidUpdate
  182. ? _chalk().default.bold.yellow(`${snapshotsOutdated} obsolete`) + ', '
  183. : '') +
  184. (snapshotsOutdated && snapshotsDidUpdate
  185. ? _chalk().default.bold.green(`${snapshotsOutdated} removed`) + ', '
  186. : '') +
  187. (snapshotsFilesRemoved && !snapshotsDidUpdate
  188. ? _chalk().default.bold.yellow(
  189. (0, _jestUtil().pluralize)('file', snapshotsFilesRemoved) +
  190. ' obsolete'
  191. ) + ', '
  192. : '') +
  193. (snapshotsFilesRemoved && snapshotsDidUpdate
  194. ? _chalk().default.bold.green(
  195. (0, _jestUtil().pluralize)('file', snapshotsFilesRemoved) + ' removed'
  196. ) + ', '
  197. : '') +
  198. (snapshotsUpdated
  199. ? _chalk().default.bold.green(`${snapshotsUpdated} updated`) + ', '
  200. : '') +
  201. (snapshotsAdded
  202. ? _chalk().default.bold.green(`${snapshotsAdded} written`) + ', '
  203. : '') +
  204. (snapshotsPassed
  205. ? _chalk().default.bold.green(`${snapshotsPassed} passed`) + ', '
  206. : '') +
  207. `${snapshotsTotal} total`;
  208. const time = renderTime(runTime, estimatedTime, width);
  209. return [suites, tests, snapshots, time].join('\n');
  210. };
  211. exports.getSummary = getSummary;
  212. const renderTime = (runTime, estimatedTime, width) => {
  213. // If we are more than one second over the estimated time, highlight it.
  214. const renderedTime =
  215. estimatedTime && runTime >= estimatedTime + 1
  216. ? _chalk().default.bold.yellow(runTime + 's')
  217. : runTime + 's';
  218. let time = _chalk().default.bold(`Time:`) + ` ${renderedTime}`;
  219. if (runTime < estimatedTime) {
  220. time += `, estimated ${estimatedTime}s`;
  221. } // Only show a progress bar if the test run is actually going to take
  222. // some time.
  223. if (estimatedTime > 2 && runTime < estimatedTime && width) {
  224. const availableWidth = Math.min(PROGRESS_BAR_WIDTH, width);
  225. const length = Math.min(
  226. Math.floor((runTime / estimatedTime) * availableWidth),
  227. availableWidth
  228. );
  229. if (availableWidth >= 2) {
  230. time +=
  231. '\n' +
  232. _chalk()
  233. .default.green('█')
  234. .repeat(length) +
  235. _chalk()
  236. .default.white('█')
  237. .repeat(availableWidth - length);
  238. }
  239. }
  240. return time;
  241. }; // word-wrap a string that contains ANSI escape sequences.
  242. // ANSI escape sequences do not add to the string length.
  243. const wrapAnsiString = (string, terminalWidth) => {
  244. if (terminalWidth === 0) {
  245. // if the terminal width is zero, don't bother word-wrapping
  246. return string;
  247. }
  248. const ANSI_REGEXP = /[\u001b\u009b]\[\d{1,2}m/g;
  249. const tokens = [];
  250. let lastIndex = 0;
  251. let match;
  252. while ((match = ANSI_REGEXP.exec(string))) {
  253. const ansi = match[0];
  254. const index = match['index'];
  255. if (index != lastIndex) {
  256. tokens.push(['string', string.slice(lastIndex, index)]);
  257. }
  258. tokens.push(['ansi', ansi]);
  259. lastIndex = index + ansi.length;
  260. }
  261. if (lastIndex != string.length - 1) {
  262. tokens.push(['string', string.slice(lastIndex, string.length)]);
  263. }
  264. let lastLineLength = 0;
  265. return tokens
  266. .reduce(
  267. (lines, [kind, token]) => {
  268. if (kind === 'string') {
  269. if (lastLineLength + token.length > terminalWidth) {
  270. while (token.length) {
  271. const chunk = token.slice(0, terminalWidth - lastLineLength);
  272. const remaining = token.slice(
  273. terminalWidth - lastLineLength,
  274. token.length
  275. );
  276. lines[lines.length - 1] += chunk;
  277. lastLineLength += chunk.length;
  278. token = remaining;
  279. if (token.length) {
  280. lines.push('');
  281. lastLineLength = 0;
  282. }
  283. }
  284. } else {
  285. lines[lines.length - 1] += token;
  286. lastLineLength += token.length;
  287. }
  288. } else {
  289. lines[lines.length - 1] += token;
  290. }
  291. return lines;
  292. },
  293. ['']
  294. )
  295. .join('\n');
  296. };
  297. exports.wrapAnsiString = wrapAnsiString;