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.
460 lines
13 KiB
460 lines
13 KiB
'use strict'; |
|
|
|
Object.defineProperty(exports, '__esModule', { |
|
value: true |
|
}); |
|
exports.testNameToKey = |
|
exports.serialize = |
|
exports.saveSnapshotFile = |
|
exports.removeLinesBeforeExternalMatcherTrap = |
|
exports.removeExtraLineBreaks = |
|
exports.minify = |
|
exports.keyToTestName = |
|
exports.getSnapshotData = |
|
exports.escapeBacktickString = |
|
exports.ensureDirectoryExists = |
|
exports.deserializeString = |
|
exports.deepMerge = |
|
exports.addExtraLineBreaks = |
|
exports.SNAPSHOT_VERSION_WARNING = |
|
exports.SNAPSHOT_VERSION = |
|
exports.SNAPSHOT_GUIDE_LINK = |
|
void 0; |
|
|
|
var path = _interopRequireWildcard(require('path')); |
|
|
|
var _chalk = _interopRequireDefault(require('chalk')); |
|
|
|
var fs = _interopRequireWildcard(require('graceful-fs')); |
|
|
|
var _naturalCompare = _interopRequireDefault(require('natural-compare')); |
|
|
|
var _prettyFormat = require('pretty-format'); |
|
|
|
var _plugins = require('./plugins'); |
|
|
|
function _interopRequireDefault(obj) { |
|
return obj && obj.__esModule ? obj : {default: obj}; |
|
} |
|
|
|
function _getRequireWildcardCache(nodeInterop) { |
|
if (typeof WeakMap !== 'function') return null; |
|
var cacheBabelInterop = new WeakMap(); |
|
var cacheNodeInterop = new WeakMap(); |
|
return (_getRequireWildcardCache = function (nodeInterop) { |
|
return nodeInterop ? cacheNodeInterop : cacheBabelInterop; |
|
})(nodeInterop); |
|
} |
|
|
|
function _interopRequireWildcard(obj, nodeInterop) { |
|
if (!nodeInterop && obj && obj.__esModule) { |
|
return obj; |
|
} |
|
if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) { |
|
return {default: obj}; |
|
} |
|
var cache = _getRequireWildcardCache(nodeInterop); |
|
if (cache && cache.has(obj)) { |
|
return cache.get(obj); |
|
} |
|
var newObj = {}; |
|
var hasPropertyDescriptor = |
|
Object.defineProperty && Object.getOwnPropertyDescriptor; |
|
for (var key in obj) { |
|
if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) { |
|
var desc = hasPropertyDescriptor |
|
? Object.getOwnPropertyDescriptor(obj, key) |
|
: null; |
|
if (desc && (desc.get || desc.set)) { |
|
Object.defineProperty(newObj, key, desc); |
|
} else { |
|
newObj[key] = obj[key]; |
|
} |
|
} |
|
} |
|
newObj.default = obj; |
|
if (cache) { |
|
cache.set(obj, newObj); |
|
} |
|
return newObj; |
|
} |
|
|
|
var global = (function () { |
|
if (typeof globalThis !== 'undefined') { |
|
return globalThis; |
|
} else if (typeof global !== 'undefined') { |
|
return global; |
|
} else if (typeof self !== 'undefined') { |
|
return self; |
|
} else if (typeof window !== 'undefined') { |
|
return window; |
|
} else { |
|
return Function('return this')(); |
|
} |
|
})(); |
|
|
|
var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; |
|
|
|
var global = (function () { |
|
if (typeof globalThis !== 'undefined') { |
|
return globalThis; |
|
} else if (typeof global !== 'undefined') { |
|
return global; |
|
} else if (typeof self !== 'undefined') { |
|
return self; |
|
} else if (typeof window !== 'undefined') { |
|
return window; |
|
} else { |
|
return Function('return this')(); |
|
} |
|
})(); |
|
|
|
var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; |
|
|
|
var global = (function () { |
|
if (typeof globalThis !== 'undefined') { |
|
return globalThis; |
|
} else if (typeof global !== 'undefined') { |
|
return global; |
|
} else if (typeof self !== 'undefined') { |
|
return self; |
|
} else if (typeof window !== 'undefined') { |
|
return window; |
|
} else { |
|
return Function('return this')(); |
|
} |
|
})(); |
|
|
|
var jestWriteFile = |
|
global[Symbol.for('jest-native-write-file')] || fs.writeFileSync; |
|
|
|
var global = (function () { |
|
if (typeof globalThis !== 'undefined') { |
|
return globalThis; |
|
} else if (typeof global !== 'undefined') { |
|
return global; |
|
} else if (typeof self !== 'undefined') { |
|
return self; |
|
} else if (typeof window !== 'undefined') { |
|
return window; |
|
} else { |
|
return Function('return this')(); |
|
} |
|
})(); |
|
|
|
var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; |
|
|
|
var global = (function () { |
|
if (typeof globalThis !== 'undefined') { |
|
return globalThis; |
|
} else if (typeof global !== 'undefined') { |
|
return global; |
|
} else if (typeof self !== 'undefined') { |
|
return self; |
|
} else if (typeof window !== 'undefined') { |
|
return window; |
|
} else { |
|
return Function('return this')(); |
|
} |
|
})(); |
|
|
|
var jestReadFile = |
|
global[Symbol.for('jest-native-read-file')] || fs.readFileSync; |
|
|
|
var global = (function () { |
|
if (typeof globalThis !== 'undefined') { |
|
return globalThis; |
|
} else if (typeof global !== 'undefined') { |
|
return global; |
|
} else if (typeof self !== 'undefined') { |
|
return self; |
|
} else if (typeof window !== 'undefined') { |
|
return window; |
|
} else { |
|
return Function('return this')(); |
|
} |
|
})(); |
|
|
|
var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol; |
|
|
|
var global = (function () { |
|
if (typeof globalThis !== 'undefined') { |
|
return globalThis; |
|
} else if (typeof global !== 'undefined') { |
|
return global; |
|
} else if (typeof self !== 'undefined') { |
|
return self; |
|
} else if (typeof window !== 'undefined') { |
|
return window; |
|
} else { |
|
return Function('return this')(); |
|
} |
|
})(); |
|
|
|
var jestExistsFile = |
|
global[Symbol.for('jest-native-exists-file')] || fs.existsSync; |
|
const SNAPSHOT_VERSION = '1'; |
|
exports.SNAPSHOT_VERSION = SNAPSHOT_VERSION; |
|
const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/; |
|
const SNAPSHOT_GUIDE_LINK = 'https://goo.gl/fbAQLP'; |
|
exports.SNAPSHOT_GUIDE_LINK = SNAPSHOT_GUIDE_LINK; |
|
|
|
const SNAPSHOT_VERSION_WARNING = _chalk.default.yellow( |
|
`${_chalk.default.bold('Warning')}: Before you upgrade snapshots, ` + |
|
'we recommend that you revert any local changes to tests or other code, ' + |
|
'to ensure that you do not store invalid state.' |
|
); |
|
|
|
exports.SNAPSHOT_VERSION_WARNING = SNAPSHOT_VERSION_WARNING; |
|
|
|
const writeSnapshotVersion = () => |
|
`// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}`; |
|
|
|
const validateSnapshotVersion = snapshotContents => { |
|
const versionTest = SNAPSHOT_VERSION_REGEXP.exec(snapshotContents); |
|
const version = versionTest && versionTest[1]; |
|
|
|
if (!version) { |
|
return new Error( |
|
_chalk.default.red( |
|
`${_chalk.default.bold( |
|
'Outdated snapshot' |
|
)}: No snapshot header found. ` + |
|
'Jest 19 introduced versioned snapshots to ensure all developers ' + |
|
'on a project are using the same version of Jest. ' + |
|
'Please update all snapshots during this upgrade of Jest.\n\n' |
|
) + SNAPSHOT_VERSION_WARNING |
|
); |
|
} |
|
|
|
if (version < SNAPSHOT_VERSION) { |
|
return new Error( |
|
_chalk.default.red( |
|
`${_chalk.default.red.bold( |
|
'Outdated snapshot' |
|
)}: The version of the snapshot ` + |
|
'file associated with this test is outdated. The snapshot file ' + |
|
'version ensures that all developers on a project are using ' + |
|
'the same version of Jest. ' + |
|
'Please update all snapshots during this upgrade of Jest.\n\n' |
|
) + |
|
`Expected: v${SNAPSHOT_VERSION}\n` + |
|
`Received: v${version}\n\n` + |
|
SNAPSHOT_VERSION_WARNING |
|
); |
|
} |
|
|
|
if (version > SNAPSHOT_VERSION) { |
|
return new Error( |
|
_chalk.default.red( |
|
`${_chalk.default.red.bold( |
|
'Outdated Jest version' |
|
)}: The version of this ` + |
|
'snapshot file indicates that this project is meant to be used ' + |
|
'with a newer version of Jest. The snapshot file version ensures ' + |
|
'that all developers on a project are using the same version of ' + |
|
'Jest. Please update your version of Jest and re-run the tests.\n\n' |
|
) + |
|
`Expected: v${SNAPSHOT_VERSION}\n` + |
|
`Received: v${version}` |
|
); |
|
} |
|
|
|
return null; |
|
}; |
|
|
|
function isObject(item) { |
|
return item != null && typeof item === 'object' && !Array.isArray(item); |
|
} |
|
|
|
const testNameToKey = (testName, count) => testName + ' ' + count; |
|
|
|
exports.testNameToKey = testNameToKey; |
|
|
|
const keyToTestName = key => { |
|
if (!/ \d+$/.test(key)) { |
|
throw new Error('Snapshot keys must end with a number.'); |
|
} |
|
|
|
return key.replace(/ \d+$/, ''); |
|
}; |
|
|
|
exports.keyToTestName = keyToTestName; |
|
|
|
const getSnapshotData = (snapshotPath, update) => { |
|
const data = Object.create(null); |
|
let snapshotContents = ''; |
|
let dirty = false; |
|
|
|
if (jestExistsFile(snapshotPath)) { |
|
try { |
|
snapshotContents = jestReadFile(snapshotPath, 'utf8'); // eslint-disable-next-line no-new-func |
|
|
|
const populate = new Function('exports', snapshotContents); |
|
populate(data); |
|
} catch {} |
|
} |
|
|
|
const validationResult = validateSnapshotVersion(snapshotContents); |
|
const isInvalid = snapshotContents && validationResult; |
|
|
|
if (update === 'none' && isInvalid) { |
|
throw validationResult; |
|
} |
|
|
|
if ((update === 'all' || update === 'new') && isInvalid) { |
|
dirty = true; |
|
} |
|
|
|
return { |
|
data, |
|
dirty |
|
}; |
|
}; // Add extra line breaks at beginning and end of multiline snapshot |
|
// to make the content easier to read. |
|
|
|
exports.getSnapshotData = getSnapshotData; |
|
|
|
const addExtraLineBreaks = string => |
|
string.includes('\n') ? `\n${string}\n` : string; // Remove extra line breaks at beginning and end of multiline snapshot. |
|
// Instead of trim, which can remove additional newlines or spaces |
|
// at beginning or end of the content from a custom serializer. |
|
|
|
exports.addExtraLineBreaks = addExtraLineBreaks; |
|
|
|
const removeExtraLineBreaks = string => |
|
string.length > 2 && string.startsWith('\n') && string.endsWith('\n') |
|
? string.slice(1, -1) |
|
: string; |
|
|
|
exports.removeExtraLineBreaks = removeExtraLineBreaks; |
|
|
|
const removeLinesBeforeExternalMatcherTrap = stack => { |
|
const lines = stack.split('\n'); |
|
|
|
for (let i = 0; i < lines.length; i += 1) { |
|
// It's a function name specified in `packages/expect/src/index.ts` |
|
// for external custom matchers. |
|
if (lines[i].includes('__EXTERNAL_MATCHER_TRAP__')) { |
|
return lines.slice(i + 1).join('\n'); |
|
} |
|
} |
|
|
|
return stack; |
|
}; |
|
|
|
exports.removeLinesBeforeExternalMatcherTrap = |
|
removeLinesBeforeExternalMatcherTrap; |
|
const escapeRegex = true; |
|
const printFunctionName = false; |
|
|
|
const serialize = (val, indent = 2, formatOverrides = {}) => |
|
normalizeNewlines( |
|
(0, _prettyFormat.format)(val, { |
|
escapeRegex, |
|
indent, |
|
plugins: (0, _plugins.getSerializers)(), |
|
printFunctionName, |
|
...formatOverrides |
|
}) |
|
); |
|
|
|
exports.serialize = serialize; |
|
|
|
const minify = val => |
|
(0, _prettyFormat.format)(val, { |
|
escapeRegex, |
|
min: true, |
|
plugins: (0, _plugins.getSerializers)(), |
|
printFunctionName |
|
}); // Remove double quote marks and unescape double quotes and backslashes. |
|
|
|
exports.minify = minify; |
|
|
|
const deserializeString = stringified => |
|
stringified.slice(1, -1).replace(/\\("|\\)/g, '$1'); |
|
|
|
exports.deserializeString = deserializeString; |
|
|
|
const escapeBacktickString = str => str.replace(/`|\\|\${/g, '\\$&'); |
|
|
|
exports.escapeBacktickString = escapeBacktickString; |
|
|
|
const printBacktickString = str => '`' + escapeBacktickString(str) + '`'; |
|
|
|
const ensureDirectoryExists = filePath => { |
|
try { |
|
fs.mkdirSync(path.join(path.dirname(filePath)), { |
|
recursive: true |
|
}); |
|
} catch {} |
|
}; |
|
|
|
exports.ensureDirectoryExists = ensureDirectoryExists; |
|
|
|
const normalizeNewlines = string => string.replace(/\r\n|\r/g, '\n'); |
|
|
|
const saveSnapshotFile = (snapshotData, snapshotPath) => { |
|
const snapshots = Object.keys(snapshotData) |
|
.sort(_naturalCompare.default) |
|
.map( |
|
key => |
|
'exports[' + |
|
printBacktickString(key) + |
|
'] = ' + |
|
printBacktickString(normalizeNewlines(snapshotData[key])) + |
|
';' |
|
); |
|
ensureDirectoryExists(snapshotPath); |
|
jestWriteFile( |
|
snapshotPath, |
|
writeSnapshotVersion() + '\n\n' + snapshots.join('\n\n') + '\n' |
|
); |
|
}; |
|
|
|
exports.saveSnapshotFile = saveSnapshotFile; |
|
|
|
const deepMergeArray = (target, source) => { |
|
const mergedOutput = Array.from(target); |
|
source.forEach((sourceElement, index) => { |
|
const targetElement = mergedOutput[index]; |
|
|
|
if (Array.isArray(target[index])) { |
|
mergedOutput[index] = deepMergeArray(target[index], sourceElement); |
|
} else if (isObject(targetElement)) { |
|
mergedOutput[index] = deepMerge(target[index], sourceElement); |
|
} else { |
|
// Source does not exist in target or target is primitive and cannot be deep merged |
|
mergedOutput[index] = sourceElement; |
|
} |
|
}); |
|
return mergedOutput; |
|
}; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types |
|
|
|
const deepMerge = (target, source) => { |
|
if (isObject(target) && isObject(source)) { |
|
const mergedOutput = {...target}; |
|
Object.keys(source).forEach(key => { |
|
if (isObject(source[key]) && !source[key].$$typeof) { |
|
if (!(key in target)) |
|
Object.assign(mergedOutput, { |
|
[key]: source[key] |
|
}); |
|
else mergedOutput[key] = deepMerge(target[key], source[key]); |
|
} else if (Array.isArray(source[key])) { |
|
mergedOutput[key] = deepMergeArray(target[key], source[key]); |
|
} else { |
|
Object.assign(mergedOutput, { |
|
[key]: source[key] |
|
}); |
|
} |
|
}); |
|
return mergedOutput; |
|
} else if (Array.isArray(target) && Array.isArray(source)) { |
|
return deepMergeArray(target, source); |
|
} |
|
|
|
return target; |
|
}; |
|
|
|
exports.deepMerge = deepMerge;
|
|
|