You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							401 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							401 lines
						
					
					
						
							11 KiB
						
					
					
				'use strict'; | 
						|
 | 
						|
Object.defineProperty(exports, '__esModule', { | 
						|
  value: true | 
						|
}); | 
						|
Object.defineProperty(exports, 'Frame', { | 
						|
  enumerable: true, | 
						|
  get: function get() { | 
						|
    return _types.Frame; | 
						|
  } | 
						|
}); | 
						|
exports.separateMessageFromStack = exports.formatResultsErrors = exports.formatStackTrace = exports.getTopFrame = exports.getStackTraceLines = exports.formatExecError = void 0; | 
						|
 | 
						|
var _fs = _interopRequireDefault(require('fs')); | 
						|
 | 
						|
var _path = _interopRequireDefault(require('path')); | 
						|
 | 
						|
var _chalk = _interopRequireDefault(require('chalk')); | 
						|
 | 
						|
var _micromatch = _interopRequireDefault(require('micromatch')); | 
						|
 | 
						|
var _slash = _interopRequireDefault(require('slash')); | 
						|
 | 
						|
var _codeFrame = require('@babel/code-frame'); | 
						|
 | 
						|
var _stackUtils = _interopRequireDefault(require('stack-utils')); | 
						|
 | 
						|
var _types = require('./types'); | 
						|
 | 
						|
function _interopRequireDefault(obj) { | 
						|
  return obj && obj.__esModule ? obj : {default: obj}; | 
						|
} | 
						|
 | 
						|
var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; | 
						|
 | 
						|
var jestReadFile = | 
						|
  global[Symbol.for('jest-native-read-file')] || _fs.default.readFileSync; | 
						|
 | 
						|
var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; | 
						|
// stack utils tries to create pretty stack by making paths relative. | 
						|
const stackUtils = new _stackUtils.default({ | 
						|
  cwd: 'something which does not exist' | 
						|
}); | 
						|
let nodeInternals = []; | 
						|
 | 
						|
try { | 
						|
  nodeInternals = _stackUtils.default.nodeInternals(); | 
						|
} catch (e) { | 
						|
  // `StackUtils.nodeInternals()` fails in browsers. We don't need to remove | 
						|
  // node internals in the browser though, so no issue. | 
						|
} | 
						|
 | 
						|
const PATH_NODE_MODULES = `${_path.default.sep}node_modules${_path.default.sep}`; | 
						|
const PATH_JEST_PACKAGES = `${_path.default.sep}jest${_path.default.sep}packages${_path.default.sep}`; // filter for noisy stack trace lines | 
						|
 | 
						|
const JASMINE_IGNORE = /^\s+at(?:(?:.jasmine\-)|\s+jasmine\.buildExpectationResult)/; | 
						|
const JEST_INTERNALS_IGNORE = /^\s+at.*?jest(-.*?)?(\/|\\)(build|node_modules|packages)(\/|\\)/; | 
						|
const ANONYMOUS_FN_IGNORE = /^\s+at <anonymous>.*$/; | 
						|
const ANONYMOUS_PROMISE_IGNORE = /^\s+at (new )?Promise \(<anonymous>\).*$/; | 
						|
const ANONYMOUS_GENERATOR_IGNORE = /^\s+at Generator.next \(<anonymous>\).*$/; | 
						|
const NATIVE_NEXT_IGNORE = /^\s+at next \(native\).*$/; | 
						|
const TITLE_INDENT = '  '; | 
						|
const MESSAGE_INDENT = '    '; | 
						|
const STACK_INDENT = '      '; | 
						|
const ANCESTRY_SEPARATOR = ' \u203A '; | 
						|
 | 
						|
const TITLE_BULLET = _chalk.default.bold('\u25cf '); | 
						|
 | 
						|
const STACK_TRACE_COLOR = _chalk.default.dim; | 
						|
const STACK_PATH_REGEXP = /\s*at.*\(?(\:\d*\:\d*|native)\)?/; | 
						|
const EXEC_ERROR_MESSAGE = 'Test suite failed to run'; | 
						|
const NOT_EMPTY_LINE_REGEXP = /^(?!$)/gm; | 
						|
 | 
						|
const indentAllLines = (lines, indent) => | 
						|
  lines.replace(NOT_EMPTY_LINE_REGEXP, indent); | 
						|
 | 
						|
const trim = string => (string || '').trim(); // Some errors contain not only line numbers in stack traces | 
						|
// e.g. SyntaxErrors can contain snippets of code, and we don't | 
						|
// want to trim those, because they may have pointers to the column/character | 
						|
// which will get misaligned. | 
						|
 | 
						|
const trimPaths = string => | 
						|
  string.match(STACK_PATH_REGEXP) ? trim(string) : string; | 
						|
 | 
						|
const getRenderedCallsite = (fileContent, line, column) => { | 
						|
  let renderedCallsite = (0, _codeFrame.codeFrameColumns)( | 
						|
    fileContent, | 
						|
    { | 
						|
      start: { | 
						|
        column, | 
						|
        line | 
						|
      } | 
						|
    }, | 
						|
    { | 
						|
      highlightCode: true | 
						|
    } | 
						|
  ); | 
						|
  renderedCallsite = indentAllLines(renderedCallsite, MESSAGE_INDENT); | 
						|
  renderedCallsite = `\n${renderedCallsite}\n`; | 
						|
  return renderedCallsite; | 
						|
}; | 
						|
 | 
						|
const blankStringRegexp = /^\s*$/; // ExecError is an error thrown outside of the test suite (not inside an `it` or | 
						|
// `before/after each` hooks). If it's thrown, none of the tests in the file | 
						|
// are executed. | 
						|
 | 
						|
const formatExecError = (error, config, options, testPath, reuseMessage) => { | 
						|
  if (!error || typeof error === 'number') { | 
						|
    error = new Error(`Expected an Error, but "${String(error)}" was thrown`); | 
						|
    error.stack = ''; | 
						|
  } | 
						|
 | 
						|
  let message, stack; | 
						|
 | 
						|
  if (typeof error === 'string' || !error) { | 
						|
    error || (error = 'EMPTY ERROR'); | 
						|
    message = ''; | 
						|
    stack = error; | 
						|
  } else { | 
						|
    message = error.message; | 
						|
    stack = error.stack; | 
						|
  } | 
						|
 | 
						|
  const separated = separateMessageFromStack(stack || ''); | 
						|
  stack = separated.stack; | 
						|
 | 
						|
  if (separated.message.includes(trim(message))) { | 
						|
    // Often stack trace already contains the duplicate of the message | 
						|
    message = separated.message; | 
						|
  } | 
						|
 | 
						|
  message = indentAllLines(message, MESSAGE_INDENT); | 
						|
  stack = | 
						|
    stack && !options.noStackTrace | 
						|
      ? '\n' + formatStackTrace(stack, config, options, testPath) | 
						|
      : ''; | 
						|
 | 
						|
  if (blankStringRegexp.test(message) && blankStringRegexp.test(stack)) { | 
						|
    // this can happen if an empty object is thrown. | 
						|
    message = MESSAGE_INDENT + 'Error: No message was provided'; | 
						|
  } | 
						|
 | 
						|
  let messageToUse; | 
						|
 | 
						|
  if (reuseMessage) { | 
						|
    messageToUse = ` ${message.trim()}`; | 
						|
  } else { | 
						|
    messageToUse = `${EXEC_ERROR_MESSAGE}\n\n${message}`; | 
						|
  } | 
						|
 | 
						|
  return TITLE_INDENT + TITLE_BULLET + messageToUse + stack + '\n'; | 
						|
}; | 
						|
 | 
						|
exports.formatExecError = formatExecError; | 
						|
 | 
						|
const removeInternalStackEntries = (lines, options) => { | 
						|
  let pathCounter = 0; | 
						|
  return lines.filter(line => { | 
						|
    if (ANONYMOUS_FN_IGNORE.test(line)) { | 
						|
      return false; | 
						|
    } | 
						|
 | 
						|
    if (ANONYMOUS_PROMISE_IGNORE.test(line)) { | 
						|
      return false; | 
						|
    } | 
						|
 | 
						|
    if (ANONYMOUS_GENERATOR_IGNORE.test(line)) { | 
						|
      return false; | 
						|
    } | 
						|
 | 
						|
    if (NATIVE_NEXT_IGNORE.test(line)) { | 
						|
      return false; | 
						|
    } | 
						|
 | 
						|
    if (nodeInternals.some(internal => internal.test(line))) { | 
						|
      return false; | 
						|
    } | 
						|
 | 
						|
    if (!STACK_PATH_REGEXP.test(line)) { | 
						|
      return true; | 
						|
    } | 
						|
 | 
						|
    if (JASMINE_IGNORE.test(line)) { | 
						|
      return false; | 
						|
    } | 
						|
 | 
						|
    if (++pathCounter === 1) { | 
						|
      return true; // always keep the first line even if it's from Jest | 
						|
    } | 
						|
 | 
						|
    if (options.noStackTrace) { | 
						|
      return false; | 
						|
    } | 
						|
 | 
						|
    if (JEST_INTERNALS_IGNORE.test(line)) { | 
						|
      return false; | 
						|
    } | 
						|
 | 
						|
    return true; | 
						|
  }); | 
						|
}; | 
						|
 | 
						|
const formatPaths = (config, relativeTestPath, line) => { | 
						|
  // Extract the file path from the trace line. | 
						|
  const match = line.match(/(^\s*at .*?\(?)([^()]+)(:[0-9]+:[0-9]+\)?.*$)/); | 
						|
 | 
						|
  if (!match) { | 
						|
    return line; | 
						|
  } | 
						|
 | 
						|
  let filePath = (0, _slash.default)( | 
						|
    _path.default.relative(config.rootDir, match[2]) | 
						|
  ); // highlight paths from the current test file | 
						|
 | 
						|
  if ( | 
						|
    (config.testMatch && | 
						|
      config.testMatch.length && | 
						|
      _micromatch.default.some(filePath, config.testMatch)) || | 
						|
    filePath === relativeTestPath | 
						|
  ) { | 
						|
    filePath = _chalk.default.reset.cyan(filePath); | 
						|
  } | 
						|
 | 
						|
  return STACK_TRACE_COLOR(match[1]) + filePath + STACK_TRACE_COLOR(match[3]); | 
						|
}; | 
						|
 | 
						|
const getStackTraceLines = ( | 
						|
  stack, | 
						|
  options = { | 
						|
    noStackTrace: false | 
						|
  } | 
						|
) => removeInternalStackEntries(stack.split(/\n/), options); | 
						|
 | 
						|
exports.getStackTraceLines = getStackTraceLines; | 
						|
 | 
						|
const getTopFrame = lines => { | 
						|
  var _iteratorNormalCompletion = true; | 
						|
  var _didIteratorError = false; | 
						|
  var _iteratorError = undefined; | 
						|
 | 
						|
  try { | 
						|
    for ( | 
						|
      var _iterator = lines[Symbol.iterator](), _step; | 
						|
      !(_iteratorNormalCompletion = (_step = _iterator.next()).done); | 
						|
      _iteratorNormalCompletion = true | 
						|
    ) { | 
						|
      const line = _step.value; | 
						|
 | 
						|
      if ( | 
						|
        line.includes(PATH_NODE_MODULES) || | 
						|
        line.includes(PATH_JEST_PACKAGES) | 
						|
      ) { | 
						|
        continue; | 
						|
      } | 
						|
 | 
						|
      const parsedFrame = stackUtils.parseLine(line.trim()); | 
						|
 | 
						|
      if (parsedFrame && parsedFrame.file) { | 
						|
        return parsedFrame; | 
						|
      } | 
						|
    } | 
						|
  } catch (err) { | 
						|
    _didIteratorError = true; | 
						|
    _iteratorError = err; | 
						|
  } finally { | 
						|
    try { | 
						|
      if (!_iteratorNormalCompletion && _iterator.return != null) { | 
						|
        _iterator.return(); | 
						|
      } | 
						|
    } finally { | 
						|
      if (_didIteratorError) { | 
						|
        throw _iteratorError; | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return null; | 
						|
}; | 
						|
 | 
						|
exports.getTopFrame = getTopFrame; | 
						|
 | 
						|
const formatStackTrace = (stack, config, options, testPath) => { | 
						|
  const lines = getStackTraceLines(stack, options); | 
						|
  const topFrame = getTopFrame(lines); | 
						|
  let renderedCallsite = ''; | 
						|
  const relativeTestPath = testPath | 
						|
    ? (0, _slash.default)(_path.default.relative(config.rootDir, testPath)) | 
						|
    : null; | 
						|
 | 
						|
  if (topFrame) { | 
						|
    const column = topFrame.column, | 
						|
      filename = topFrame.file, | 
						|
      line = topFrame.line; | 
						|
 | 
						|
    if (line && filename && _path.default.isAbsolute(filename)) { | 
						|
      let fileContent; | 
						|
 | 
						|
      try { | 
						|
        // TODO: check & read HasteFS instead of reading the filesystem: | 
						|
        // see: https://github.com/facebook/jest/pull/5405#discussion_r164281696 | 
						|
        fileContent = jestReadFile(filename, 'utf8'); | 
						|
        renderedCallsite = getRenderedCallsite(fileContent, line, column); | 
						|
      } catch (e) { | 
						|
        // the file does not exist or is inaccessible, we ignore | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  const stacktrace = lines | 
						|
    .filter(Boolean) | 
						|
    .map( | 
						|
      line => | 
						|
        STACK_INDENT + formatPaths(config, relativeTestPath, trimPaths(line)) | 
						|
    ) | 
						|
    .join('\n'); | 
						|
  return `${renderedCallsite}\n${stacktrace}`; | 
						|
}; | 
						|
 | 
						|
exports.formatStackTrace = formatStackTrace; | 
						|
 | 
						|
const formatResultsErrors = (testResults, config, options, testPath) => { | 
						|
  const failedResults = testResults.reduce((errors, result) => { | 
						|
    result.failureMessages.forEach(content => | 
						|
      errors.push({ | 
						|
        content, | 
						|
        result | 
						|
      }) | 
						|
    ); | 
						|
    return errors; | 
						|
  }, []); | 
						|
 | 
						|
  if (!failedResults.length) { | 
						|
    return null; | 
						|
  } | 
						|
 | 
						|
  return failedResults | 
						|
    .map(({result, content}) => { | 
						|
      let _separateMessageFromS = separateMessageFromStack(content), | 
						|
        message = _separateMessageFromS.message, | 
						|
        stack = _separateMessageFromS.stack; | 
						|
 | 
						|
      stack = options.noStackTrace | 
						|
        ? '' | 
						|
        : STACK_TRACE_COLOR( | 
						|
            formatStackTrace(stack, config, options, testPath) | 
						|
          ) + '\n'; | 
						|
      message = indentAllLines(message, MESSAGE_INDENT); | 
						|
      const title = | 
						|
        _chalk.default.bold.red( | 
						|
          TITLE_INDENT + | 
						|
            TITLE_BULLET + | 
						|
            result.ancestorTitles.join(ANCESTRY_SEPARATOR) + | 
						|
            (result.ancestorTitles.length ? ANCESTRY_SEPARATOR : '') + | 
						|
            result.title | 
						|
        ) + '\n'; | 
						|
      return title + '\n' + message + '\n' + stack; | 
						|
    }) | 
						|
    .join('\n'); | 
						|
}; | 
						|
 | 
						|
exports.formatResultsErrors = formatResultsErrors; | 
						|
const errorRegexp = /^Error:?\s*$/; | 
						|
 | 
						|
const removeBlankErrorLine = str => | 
						|
  str | 
						|
    .split('\n') // Lines saying just `Error:` are useless | 
						|
    .filter(line => !errorRegexp.test(line)) | 
						|
    .join('\n') | 
						|
    .trimRight(); // jasmine and worker farm sometimes don't give us access to the actual | 
						|
// Error object, so we have to regexp out the message from the stack string | 
						|
// to format it. | 
						|
 | 
						|
const separateMessageFromStack = content => { | 
						|
  if (!content) { | 
						|
    return { | 
						|
      message: '', | 
						|
      stack: '' | 
						|
    }; | 
						|
  } // All lines up to what looks like a stack -- or if nothing looks like a stack | 
						|
  // (maybe it's a code frame instead), just the first non-empty line. | 
						|
  // If the error is a plain "Error:" instead of a SyntaxError or TypeError we | 
						|
  // remove the prefix from the message because it is generally not useful. | 
						|
 | 
						|
  const messageMatch = content.match( | 
						|
    /^(?:Error: )?([\s\S]*?(?=\n\s*at\s.*:\d*:\d*)|\s*.*)([\s\S]*)$/ | 
						|
  ); | 
						|
 | 
						|
  if (!messageMatch) { | 
						|
    // For typescript | 
						|
    throw new Error('If you hit this error, the regex above is buggy.'); | 
						|
  } | 
						|
 | 
						|
  const message = removeBlankErrorLine(messageMatch[1]); | 
						|
  const stack = removeBlankErrorLine(messageMatch[2]); | 
						|
  return { | 
						|
    message, | 
						|
    stack | 
						|
  }; | 
						|
}; | 
						|
 | 
						|
exports.separateMessageFromStack = separateMessageFromStack;
 | 
						|
 |