123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- 'use strict';
- var _fs = _interopRequireDefault(require('fs'));
- var _jestMatcherUtils = require('jest-matcher-utils');
- var _snapshot_resolver = require('./snapshot_resolver');
- var _State = _interopRequireDefault(require('./State'));
- var _plugins = require('./plugins');
- var _print = require('./print');
- var utils = _interopRequireWildcard(require('./utils'));
- function _interopRequireWildcard(obj) {
- if (obj && obj.__esModule) {
- return obj;
- } else {
- var newObj = {};
- if (obj != null) {
- for (var key in obj) {
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
- var desc =
- Object.defineProperty && Object.getOwnPropertyDescriptor
- ? Object.getOwnPropertyDescriptor(obj, key)
- : {};
- if (desc.get || desc.set) {
- Object.defineProperty(newObj, key, desc);
- } else {
- newObj[key] = obj[key];
- }
- }
- }
- }
- newObj.default = obj;
- return newObj;
- }
- }
- function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : {default: obj};
- }
- var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
- var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
- var jestExistsFile =
- global[Symbol.for('jest-native-exists-file')] || _fs.default.existsSync;
- const DID_NOT_THROW = 'Received function did not throw'; // same as toThrow
- const NOT_SNAPSHOT_MATCHERS = `.${(0, _jestMatcherUtils.BOLD_WEIGHT)(
- 'not'
- )} cannot be used with snapshot matchers`;
- const HINT_ARG = 'hint';
- const HINT_COLOR = _jestMatcherUtils.BOLD_WEIGHT;
- const INLINE_SNAPSHOT_ARG = 'snapshot';
- const PROPERTY_MATCHERS_ARG = 'properties';
- const INDENTATION_REGEX = /^([^\S\n]*)\S/m; // Display name in report when matcher fails same as in snapshot file,
- // but with optional hint argument in bold weight.
- const printName = (concatenatedBlockNames = '', hint = '', count) => {
- const hasNames = concatenatedBlockNames.length !== 0;
- const hasHint = hint.length !== 0;
- return (
- '`' +
- (hasNames ? utils.escapeBacktickString(concatenatedBlockNames) : '') +
- (hasNames && hasHint ? ': ' : '') +
- (hasHint
- ? (0, _jestMatcherUtils.BOLD_WEIGHT)(utils.escapeBacktickString(hint))
- : '') +
- ' ' +
- count +
- '`'
- );
- };
- function stripAddedIndentation(inlineSnapshot) {
- // Find indentation if exists.
- const match = inlineSnapshot.match(INDENTATION_REGEX);
- if (!match || !match[1]) {
- // No indentation.
- return inlineSnapshot;
- }
- const indentation = match[1];
- const lines = inlineSnapshot.split('\n');
- if (lines.length <= 2) {
- // Must be at least 3 lines.
- return inlineSnapshot;
- }
- if (lines[0].trim() !== '' || lines[lines.length - 1].trim() !== '') {
- // If not blank first and last lines, abort.
- return inlineSnapshot;
- }
- for (let i = 1; i < lines.length - 1; i++) {
- if (lines[i] !== '') {
- if (lines[i].indexOf(indentation) !== 0) {
- // All lines except first and last should either be blank or have the same
- // indent as the first line (or more). If this isn't the case we don't
- // want to touch the snapshot at all.
- return inlineSnapshot;
- }
- lines[i] = lines[i].substr(indentation.length);
- }
- } // Last line is a special case because it won't have the same indent as others
- // but may still have been given some indent to line up.
- lines[lines.length - 1] = ''; // Return inline snapshot, now at indent 0.
- inlineSnapshot = lines.join('\n');
- return inlineSnapshot;
- }
- const fileExists = (filePath, hasteFS) =>
- hasteFS.exists(filePath) || jestExistsFile(filePath);
- const cleanup = (hasteFS, update, snapshotResolver, testPathIgnorePatterns) => {
- const pattern = '\\.' + _snapshot_resolver.EXTENSION + '$';
- const files = hasteFS.matchFiles(pattern);
- let testIgnorePatternsRegex = null;
- if (testPathIgnorePatterns && testPathIgnorePatterns.length > 0) {
- testIgnorePatternsRegex = new RegExp(testPathIgnorePatterns.join('|'));
- }
- const list = files.filter(snapshotFile => {
- const testPath = snapshotResolver.resolveTestPath(snapshotFile); // ignore snapshots of ignored tests
- if (testIgnorePatternsRegex && testIgnorePatternsRegex.test(testPath)) {
- return false;
- }
- if (!fileExists(testPath, hasteFS)) {
- if (update === 'all') {
- _fs.default.unlinkSync(snapshotFile);
- }
- return true;
- }
- return false;
- });
- return {
- filesRemoved: list.length,
- filesRemovedList: list
- };
- };
- const toMatchSnapshot = function toMatchSnapshot(
- received,
- propertyMatchers,
- hint
- ) {
- const matcherName = 'toMatchSnapshot';
- let expectedArgument = '';
- let secondArgument = '';
- if (typeof propertyMatchers === 'object' && propertyMatchers !== null) {
- expectedArgument = PROPERTY_MATCHERS_ARG;
- if (typeof hint === 'string' && hint.length !== 0) {
- secondArgument = HINT_ARG;
- }
- } else if (
- typeof propertyMatchers === 'string' &&
- propertyMatchers.length !== 0
- ) {
- expectedArgument = HINT_ARG;
- }
- const options = {
- isNot: this.isNot,
- promise: this.promise,
- secondArgument
- };
- if (expectedArgument === HINT_ARG) {
- options.expectedColor = HINT_COLOR;
- }
- if (secondArgument === HINT_ARG) {
- options.secondArgumentColor = HINT_COLOR;
- }
- if (arguments.length === 3 && !propertyMatchers) {
- throw new Error(
- 'Property matchers must be an object.\n\nTo provide a snapshot test name without property matchers, use: toMatchSnapshot("name")'
- );
- }
- return _toMatchSnapshot({
- context: this,
- expectedArgument,
- hint,
- matcherName,
- options,
- propertyMatchers,
- received
- });
- };
- const toMatchInlineSnapshot = function toMatchInlineSnapshot(
- received,
- propertyMatchersOrInlineSnapshot,
- inlineSnapshot
- ) {
- const matcherName = 'toMatchInlineSnapshot';
- let expectedArgument = '';
- let secondArgument = '';
- if (typeof propertyMatchersOrInlineSnapshot === 'string') {
- expectedArgument = INLINE_SNAPSHOT_ARG;
- } else if (
- typeof propertyMatchersOrInlineSnapshot === 'object' &&
- propertyMatchersOrInlineSnapshot !== null
- ) {
- expectedArgument = PROPERTY_MATCHERS_ARG;
- if (typeof inlineSnapshot === 'string') {
- secondArgument = INLINE_SNAPSHOT_ARG;
- }
- }
- const options = {
- isNot: this.isNot,
- promise: this.promise,
- secondArgument
- };
- let propertyMatchers;
- if (typeof propertyMatchersOrInlineSnapshot === 'string') {
- inlineSnapshot = propertyMatchersOrInlineSnapshot;
- } else {
- propertyMatchers = propertyMatchersOrInlineSnapshot;
- }
- return _toMatchSnapshot({
- context: this,
- expectedArgument,
- inlineSnapshot: stripAddedIndentation(inlineSnapshot || ''),
- matcherName,
- options,
- propertyMatchers,
- received
- });
- };
- const _toMatchSnapshot = ({
- context,
- expectedArgument,
- hint,
- inlineSnapshot,
- matcherName,
- options,
- propertyMatchers,
- received
- }) => {
- context.dontThrow && context.dontThrow();
- hint = typeof propertyMatchers === 'string' ? propertyMatchers : hint;
- const currentTestName = context.currentTestName,
- isNot = context.isNot,
- snapshotState = context.snapshotState;
- if (isNot) {
- throw new Error(
- (0, _jestMatcherUtils.matcherHint)(
- matcherName,
- undefined,
- expectedArgument,
- options
- ) +
- '\n\n' +
- NOT_SNAPSHOT_MATCHERS
- );
- }
- if (!snapshotState) {
- throw new Error(
- (0, _jestMatcherUtils.matcherHint)(
- matcherName,
- undefined,
- expectedArgument,
- options
- ) + '\n\nsnapshot state must be initialized'
- );
- }
- const fullTestName =
- currentTestName && hint
- ? `${currentTestName}: ${hint}`
- : currentTestName || ''; // future BREAKING change: || hint
- if (typeof propertyMatchers === 'object') {
- if (propertyMatchers === null) {
- throw new Error(`Property matchers must be an object.`);
- }
- const propertyPass = context.equals(received, propertyMatchers, [
- context.utils.iterableEquality,
- context.utils.subsetEquality
- ]);
- if (!propertyPass) {
- const key = snapshotState.fail(fullTestName, received);
- const matched = /(\d+)$/.exec(key);
- const count = matched === null ? 1 : Number(matched[1]);
- const report = () =>
- `Snapshot name: ${printName(currentTestName, hint, count)}\n` +
- '\n' +
- `Expected properties: ${context.utils.printExpected(
- propertyMatchers
- )}\n` +
- `Received value: ${context.utils.printReceived(received)}`;
- return {
- message: () =>
- (0, _jestMatcherUtils.matcherHint)(
- matcherName,
- undefined,
- expectedArgument,
- options
- ) +
- '\n\n' +
- report(),
- name: matcherName,
- pass: false,
- report
- };
- } else {
- received = utils.deepMerge(received, propertyMatchers);
- }
- }
- const result = snapshotState.match({
- error: context.error,
- inlineSnapshot,
- received,
- testName: fullTestName
- });
- const count = result.count,
- pass = result.pass;
- let actual = result.actual,
- expected = result.expected;
- let report;
- if (pass) {
- return {
- message: () => '',
- pass: true
- };
- } else if (!expected) {
- report = () =>
- `New snapshot was ${(0, _jestMatcherUtils.RECEIVED_COLOR)(
- 'not written'
- )}. The update flag ` +
- `must be explicitly passed to write a new snapshot.\n\n` +
- `This is likely because this test is run in a continuous integration ` +
- `(CI) environment in which snapshots are not written by default.\n\n` +
- `${(0, _jestMatcherUtils.RECEIVED_COLOR)('Received value')} ` +
- `${actual}`;
- } else {
- expected = (expected || '').trim();
- actual = (actual || '').trim(); // Assign to local variable because of declaration let expected:
- // TypeScript thinks it could change before report function is called.
- const printed = (0, _print.printDiffOrStringified)(
- expected,
- actual,
- received,
- 'Snapshot',
- 'Received',
- snapshotState.expand
- );
- report = () =>
- `Snapshot name: ${printName(currentTestName, hint, count)}\n\n` + printed;
- } // Passing the actual and expected objects so that a custom reporter
- // could access them, for example in order to display a custom visual diff,
- // or create a different error message
- return {
- actual,
- expected,
- message: () =>
- (0, _jestMatcherUtils.matcherHint)(
- matcherName,
- undefined,
- expectedArgument,
- options
- ) +
- '\n\n' +
- report(),
- name: matcherName,
- pass: false,
- report
- };
- };
- const toThrowErrorMatchingSnapshot = function toThrowErrorMatchingSnapshot(
- received,
- hint, // because error TS1016 for hint?: string
- fromPromise
- ) {
- const matcherName = 'toThrowErrorMatchingSnapshot';
- const expectedArgument =
- typeof hint === 'string' && hint.length !== 0 ? HINT_ARG : '';
- const options = {
- expectedColor: HINT_COLOR,
- isNot: this.isNot,
- promise: this.promise,
- secondArgument: ''
- };
- return _toThrowErrorMatchingSnapshot(
- {
- context: this,
- expectedArgument,
- hint,
- matcherName,
- options,
- received
- },
- fromPromise
- );
- };
- const toThrowErrorMatchingInlineSnapshot = function toThrowErrorMatchingInlineSnapshot(
- received,
- inlineSnapshot,
- fromPromise
- ) {
- const matcherName = 'toThrowErrorMatchingInlineSnapshot';
- const expectedArgument =
- typeof inlineSnapshot === 'string' ? INLINE_SNAPSHOT_ARG : '';
- const options = {
- isNot: this.isNot,
- promise: this.promise,
- secondArgument: ''
- };
- return _toThrowErrorMatchingSnapshot(
- {
- context: this,
- expectedArgument,
- inlineSnapshot: inlineSnapshot || '',
- matcherName,
- options,
- received
- },
- fromPromise
- );
- };
- const _toThrowErrorMatchingSnapshot = (
- {
- context,
- expectedArgument,
- inlineSnapshot,
- matcherName,
- options,
- received,
- hint
- },
- fromPromise
- ) => {
- context.dontThrow && context.dontThrow();
- const isNot = context.isNot;
- if (isNot) {
- throw new Error(
- (0, _jestMatcherUtils.matcherHint)(
- matcherName,
- undefined,
- expectedArgument,
- options
- ) +
- '\n\n' +
- NOT_SNAPSHOT_MATCHERS
- );
- }
- let error;
- if (fromPromise) {
- error = received;
- } else {
- try {
- received();
- } catch (e) {
- error = e;
- }
- }
- if (error === undefined) {
- throw new Error(
- (0, _jestMatcherUtils.matcherHint)(
- matcherName,
- undefined,
- expectedArgument,
- options
- ) +
- '\n\n' +
- DID_NOT_THROW
- );
- }
- return _toMatchSnapshot({
- context,
- expectedArgument,
- hint,
- inlineSnapshot,
- matcherName,
- options,
- received: error.message
- });
- };
- const JestSnapshot = {
- EXTENSION: _snapshot_resolver.EXTENSION,
- SnapshotState: _State.default,
- addSerializer: _plugins.addSerializer,
- buildSnapshotResolver: _snapshot_resolver.buildSnapshotResolver,
- cleanup,
- getSerializers: _plugins.getSerializers,
- isSnapshotPath: _snapshot_resolver.isSnapshotPath,
- toMatchInlineSnapshot,
- toMatchSnapshot,
- toThrowErrorMatchingInlineSnapshot,
- toThrowErrorMatchingSnapshot,
- utils
- };
- /* eslint-disable-next-line no-redeclare */
- module.exports = JestSnapshot;
|