Add resolver for custom snapshots paths (#6143)

* Add resolver for snapshot paths

* Remove snapshotResolver from versioned doc

* Simplify resolveSnapshotPath

* Make error feedback more actionable

* Assert test result before snapshot file

Should help troubleshoot failing tests on CI

* Run integration test with correct flags

Same as the base snapshot.test.js

* Add tests for malformed resolver module

* Resolve paths in tests like implementation

To avoid cross-platform mismatches

* Rename integration-tests => e2e

* Fix code review feedback

* Add changelog entry

* Fix review comments for e2e/__tests__/snapshot_resolver.test.js

* Fix prettier error

* Move changelog entry to correct place

* Move up type import above normal imports

* Add consistency check

* Update snapshot_resolver.js
This commit is contained in:
Nicklas Gummesson 2018-09-25 23:47:28 -07:00 committed by Simen Bekkhus
parent 2b216e4d06
commit 8eefa9690e
32 changed files with 396 additions and 53 deletions

View File

@ -6,6 +6,7 @@
- `[jest-haste-map]` Add `getFileIterator` to `HasteFS` for faster file iteration ([#7010](https://github.com/facebook/jest/pull/7010)).
- `[jest-worker]` [**BREAKING**] Add functionality to call a `setup` method in the worker before the first call and a `teardown` method when ending the farm ([#7014](https://github.com/facebook/jest/pull/7014)).
- `[jest-config]` [**BREAKING**] Set default `notifyMode` to `failure-change` ([#7024](https://github.com/facebook/jest/pull/7024))
- `[jest-snapshot]` Enable configurable snapshot paths ([#6143](https://github.com/facebook/jest/pull/6143))
### Fixes

View File

@ -102,6 +102,7 @@ const DEFAULT_PROJECT_CONFIG: ProjectConfig = {
setupTestFrameworkScriptFile: null,
skipFilter: false,
skipNodeResolution: false,
snapshotResolver: null,
snapshotSerializers: [],
testEnvironment: 'node',
testEnvironmentOptions: {},

View File

@ -642,6 +642,29 @@ If you want this path to be [relative to the root directory of your project](#ro
For example, Jest ships with several plug-ins to `jasmine` that work by monkey-patching the jasmine API. If you wanted to add even more jasmine plugins to the mix (or if you wanted some custom, project-wide matchers for example), you could do so in this module.
### `snapshotResolver` [string]
Default: `undefined`
The path to a module that can resolve test<->snapshot path. This config option lets you customize where Jest stores that snapshot files on disk.
Example snapshot resolver module:
```js
// my-snapshot-resolver-module
module.exports = {
// resolves from test to snapshot path
resolveSnapshotPath: (testPath, snapshotExtension) =>
testPath.replace('__tests__', '__snapshots__') + snapshotExtension,
// resolves from snapshot to test path
resolveTestPath: (snapshotFilePath, snapshotExtension) =>
snapshotFilePath
.replace('__snapshots__', '__tests__')
.slice(0, -snapshotExtension.length),
};
```
### `snapshotSerializers` [array<string>]
Default: `[]`

View File

@ -0,0 +1,40 @@
/**
* @flow
*/
'use strict';
const fs = require('fs');
const path = require('path');
const runJest = require('../runJest');
const snapshotDir = path.resolve(
__dirname,
'../snapshot-resolver/__snapshots__',
);
const snapshotFile = path.resolve(snapshotDir, 'snapshot.test.js.snap');
describe('Custom snapshot resolver', () => {
const cleanup = () => {
if (fs.existsSync(snapshotFile)) {
fs.unlinkSync(snapshotFile);
}
if (fs.existsSync(snapshotDir)) {
fs.rmdirSync(snapshotDir);
}
};
beforeEach(cleanup);
afterAll(cleanup);
it('Resolves snapshot files using custom resolver', () => {
const result = runJest('snapshot-resolver', ['-w=1', '--ci=false']);
expect(result.stderr).toMatch('1 snapshot written from 1 test suite');
// $FlowFixMe dynamic require
const content = require(snapshotFile);
expect(content).toHaveProperty(
'snapshots are written to custom location 1',
);
});
});

View File

@ -0,0 +1,3 @@
test('snapshots are written to custom location', () => {
expect('foobar').toMatchSnapshot();
});

View File

@ -0,0 +1,9 @@
module.exports = {
resolveSnapshotPath: (testPath, snapshotExtension) =>
testPath.replace('__tests__', '__snapshots__') + snapshotExtension,
resolveTestPath: (snapshotFilePath, snapshotExtension) =>
snapshotFilePath
.replace('__snapshots__', '__tests__')
.slice(0, -snapshotExtension.length),
};

View File

@ -0,0 +1,6 @@
{
"jest": {
"testEnvironment": "node",
"snapshotResolver": "<rootDir>/customSnapshotResolver.js"
}
}

View File

@ -42,6 +42,7 @@ module.exports = {
'/packages/jest-runtime/src/__tests__/module_dir/',
'/packages/jest-runtime/src/__tests__/NODE_PATH_dir',
'/packages/jest-snapshot/src/__tests__/plugins',
'/packages/jest-snapshot/src/__tests__/fixtures/',
'/packages/jest-validate/src/__tests__/fixtures/',
'/packages/jest-worker/src/__performance_tests__',
'/packages/pretty-format/perf/test.js',

View File

@ -13,7 +13,11 @@ import type {Event, TestEntry} from 'types/Circus';
import {extractExpectedAssertionsErrors, getState, setState} from 'expect';
import {formatExecError, formatResultsErrors} from 'jest-message-util';
import {SnapshotState, addSerializer} from 'jest-snapshot';
import {
SnapshotState,
addSerializer,
buildSnapshotResolver,
} from 'jest-snapshot';
import {addEventHandler, dispatch, ROOT_DESCRIBE_BLOCK_NAME} from '../state';
import {getTestID, getOriginalPromise} from '../utils';
import run from '../run';
@ -96,7 +100,9 @@ export const initialize = ({
});
const {expand, updateSnapshot} = globalConfig;
const snapshotState = new SnapshotState(testPath, {
const snapshotResolver = buildSnapshotResolver(config);
const snapshotPath = snapshotResolver.resolveSnapshotPath(testPath);
const snapshotState = new SnapshotState(snapshotPath, {
expand,
getBabelTraverse,
getPrettier,

View File

@ -18,6 +18,7 @@ import DependencyResolver from 'jest-resolve-dependencies';
import testPathPatternToRegExp from './testPathPatternToRegexp';
import {escapePathForRegex} from 'jest-regex-util';
import {replaceRootDirInPath} from 'jest-config';
import {buildSnapshotResolver} from 'jest-snapshot';
type SearchResult = {|
noSCM?: boolean,
@ -153,6 +154,7 @@ export default class SearchSource {
const dependencyResolver = new DependencyResolver(
this._context.resolver,
this._context.hasteFS,
buildSnapshotResolver(this._context.config),
);
const tests = toTests(

View File

@ -161,6 +161,7 @@ export default class TestScheduler {
const status = snapshot.cleanup(
context.hasteFS,
this._globalConfig.updateSnapshot,
snapshot.buildSnapshotResolver(context.config),
);
aggregatedResults.snapshot.filesRemoved += status.filesRemoved;

View File

@ -8,7 +8,7 @@
*/
import type {GlobalConfig, ProjectConfig} from 'types/Config';
import Snapshot from 'jest-snapshot';
import {isSnapshotPath} from 'jest-snapshot';
export default function isValidPath(
globalConfig: GlobalConfig,
@ -18,6 +18,6 @@ export default function isValidPath(
return (
!filePath.includes(globalConfig.coverageDirectory) &&
!config.watchPathIgnorePatterns.some(pattern => filePath.match(pattern)) &&
!filePath.endsWith(`.${Snapshot.EXTENSION}`)
!isSnapshotPath(filePath)
);
}

View File

@ -91,6 +91,7 @@ export default ({
silent: true,
skipFilter: false,
skipNodeResolution: false,
snapshotResolver: '<rootDir>/snapshotResolver.js',
snapshotSerializers: ['my-serializer-module'],
testEnvironment: 'jest-environment-jsdom',
testEnvironmentOptions: {userAgent: 'Agent/007'},

View File

@ -185,6 +185,7 @@ const getConfigs = (
setupTestFrameworkScriptFile: options.setupTestFrameworkScriptFile,
skipFilter: options.skipFilter,
skipNodeResolution: options.skipNodeResolution,
snapshotResolver: options.snapshotResolver,
snapshotSerializers: options.snapshotSerializers,
testEnvironment: options.testEnvironment,
testEnvironmentOptions: options.testEnvironmentOptions,

View File

@ -453,6 +453,7 @@ export default function normalize(options: InitialOptions, argv: Argv) {
case 'moduleLoader':
case 'runner':
case 'setupTestFrameworkScriptFile':
case 'snapshotResolver':
case 'testResultsProcessor':
case 'testRunner':
case 'filter':

View File

@ -10,9 +10,11 @@
'use strict';
import type {ProjectConfig} from 'types/Config';
import traverse from 'babel-traverse';
import {getASTfor} from './parsers/babylon_parser';
import {utils} from 'jest-snapshot';
import {buildSnapshotResolver, utils} from 'jest-snapshot';
type Node = any;
@ -95,11 +97,17 @@ const buildName: (
export default class Snapshot {
_parser: Function;
_matchers: Array<string>;
constructor(parser: any, customMatchers?: Array<string>) {
_projectConfig: ?ProjectConfig;
constructor(
parser: any,
customMatchers?: Array<string>,
projectConfig?: ProjectConfig,
) {
this._parser = parser || getASTfor;
this._matchers = ['toMatchSnapshot', 'toThrowErrorMatchingSnapshot'].concat(
customMatchers || [],
);
this._projectConfig = projectConfig;
}
getMetadata(filePath: string): Array<SnapshotMetadata> {
@ -127,7 +135,9 @@ export default class Snapshot {
},
});
const snapshotPath = utils.getSnapshotPath(filePath);
// NOTE if no projectConfig is given the default resolver will be used
const snapshotResolver = buildSnapshotResolver(this._projectConfig || {});
const snapshotPath = snapshotResolver.resolveSnapshotPath(filePath);
const snapshots = utils.getSnapshotData(snapshotPath, 'none').data;
let lastParent = null;
let count = 1;

View File

@ -11,7 +11,11 @@ import type {GlobalConfig, Path, ProjectConfig} from 'types/Config';
import type {Plugin} from 'types/PrettyFormat';
import {extractExpectedAssertionsErrors, getState, setState} from 'expect';
import {SnapshotState, addSerializer} from 'jest-snapshot';
import {
buildSnapshotResolver,
SnapshotState,
addSerializer,
} from 'jest-snapshot';
export type SetupOptions = {|
config: ProjectConfig,
@ -96,9 +100,12 @@ export default ({
.forEach(path => {
addSerializer(localRequire(path));
});
patchJasmine();
const {expand, updateSnapshot} = globalConfig;
const snapshotState = new SnapshotState(testPath, {
const snapshotResolver = buildSnapshotResolver(config);
const snapshotPath = snapshotResolver.resolveSnapshotPath(testPath);
const snapshotState = new SnapshotState(snapshotPath, {
expand,
getBabelTraverse: () => require('babel-traverse').default,
getPrettier: () =>

View File

@ -9,6 +9,7 @@
const path = require('path');
const {normalize} = require('jest-config');
const {buildSnapshotResolver} = require('jest-snapshot');
const DependencyResolver = require('../index');
const maxWorkers = 1;
@ -34,6 +35,7 @@ beforeEach(() => {
dependencyResolver = new DependencyResolver(
hasteMap.resolver,
hasteMap.hasteFS,
buildSnapshotResolver(config),
);
});
});

View File

@ -10,16 +10,8 @@
import type {HasteFS} from 'types/HasteMap';
import type {Path} from 'types/Config';
import type {Resolver, ResolveModuleConfig} from 'types/Resolve';
import Snapshot from 'jest-snapshot';
import {replacePathSepForRegex} from 'jest-regex-util';
const snapshotDirRegex = new RegExp(replacePathSepForRegex('/__snapshots__/'));
const snapshotFileRegex = new RegExp(
replacePathSepForRegex(`__snapshots__/(.*).${Snapshot.EXTENSION}`),
);
const isSnapshotPath = (path: string): boolean =>
!!path.match(snapshotDirRegex);
import type {SnapshotResolver} from 'types/SnapshotResolver';
import {isSnapshotPath} from 'jest-snapshot';
/**
* DependencyResolver is used to resolve the direct dependencies of a module or
@ -28,10 +20,16 @@ const isSnapshotPath = (path: string): boolean =>
class DependencyResolver {
_hasteFS: HasteFS;
_resolver: Resolver;
_snapshotResolver: SnapshotResolver;
constructor(resolver: Resolver, hasteFS: HasteFS) {
constructor(
resolver: Resolver,
hasteFS: HasteFS,
snapshotResolver: SnapshotResolver,
) {
this._resolver = resolver;
this._hasteFS = hasteFS;
this._snapshotResolver = snapshotResolver;
}
resolve(file: Path, options?: ResolveModuleConfig): Array<Path> {
@ -89,10 +87,8 @@ class DependencyResolver {
const changed = new Set();
for (const path of paths) {
if (this._hasteFS.exists(path)) {
// /path/to/__snapshots__/test.js.snap is always adjacent to
// /path/to/test.js
const modulePath = isSnapshotPath(path)
? path.replace(snapshotFileRegex, '$1')
? this._snapshotResolver.resolveTestPath(path)
: path;
changed.add(modulePath);
if (filter(modulePath)) {

View File

@ -14,7 +14,6 @@ import {getTopFrame, getStackTraceLines} from 'jest-message-util';
import {
saveSnapshotFile,
getSnapshotData,
getSnapshotPath,
keyToTestName,
serialize,
testNameToKey,
@ -26,7 +25,6 @@ export type SnapshotStateOptions = {|
updateSnapshot: SnapshotUpdateState,
getPrettier: () => null | any,
getBabelTraverse: () => Function,
snapshotPath?: string,
expand?: boolean,
|};
@ -55,8 +53,8 @@ export default class SnapshotState {
unmatched: number;
updated: number;
constructor(testPath: Path, options: SnapshotStateOptions) {
this._snapshotPath = options.snapshotPath || getSnapshotPath(testPath);
constructor(snapshotPath: Path, options: SnapshotStateOptions) {
this._snapshotPath = snapshotPath;
const {data, dirty} = getSnapshotData(
this._snapshotPath,
options.updateSnapshot,

View File

@ -0,0 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`malformed custom resolver in project config inconsistent functions throws 1`] = `"<bold>Custom snapshot resolver functions must transform paths consistently, i.e. expects resolveTestPath(resolveSnapshotPath('some-path/__tests__/snapshot_resolver.test.js')) === some-path/__SPECS__/snapshot_resolver.test.js</>"`;
exports[`malformed custom resolver in project config missing resolveSnapshotPath throws 1`] = `
"<bold>Custom snapshot resolver must implement a \`resolveSnapshotPath\` function.</>
Documentation: https://facebook.github.io/jest/docs/en/configuration.html#snapshotResolver"
`;
exports[`malformed custom resolver in project config missing resolveTestPath throws 1`] = `
"<bold>Custom snapshot resolver must implement a \`resolveTestPath\` function.</>
Documentation: https://facebook.github.io/jest/docs/en/configuration.html#snapshotResolver"
`;

View File

@ -0,0 +1,9 @@
module.exports = {
resolveSnapshotPath: (testPath, snapshotExtension) =>
testPath.replace('__tests__', '__snapshots__') + snapshotExtension,
resolveTestPath: (snapshotFilePath, snapshotExtension) =>
snapshotFilePath
.replace('__snapshots__', '__SPECS__')
.slice(0, -snapshotExtension.length),
};

View File

@ -0,0 +1,3 @@
module.exports = {
resolveTestPath: () => {},
};

View File

@ -0,0 +1,3 @@
module.exports = {
resolveSnapshotPath: () => {},
};

View File

@ -0,0 +1,9 @@
module.exports = {
resolveSnapshotPath: (testPath, snapshotExtension) =>
testPath.replace('__tests__', '__snapshots__') + snapshotExtension,
resolveTestPath: (snapshotFilePath, snapshotExtension) =>
snapshotFilePath
.replace('__snapshots__', '__tests__')
.slice(0, -snapshotExtension.length),
};

View File

@ -0,0 +1,108 @@
const path = require('path');
const {buildSnapshotResolver} = require('../snapshot_resolver');
describe('defaults', () => {
let snapshotResolver;
const projectConfig = {
rootDir: 'default',
// snapshotResolver: null,
};
beforeEach(() => {
snapshotResolver = buildSnapshotResolver(projectConfig);
});
it('returns cached object if called multiple times', () => {
expect(buildSnapshotResolver(projectConfig)).toBe(snapshotResolver);
});
it('resolveSnapshotPath()', () => {
expect(snapshotResolver.resolveSnapshotPath('/abc/cde/a.test.js')).toBe(
path.join('/abc', 'cde', '__snapshots__', 'a.test.js.snap'),
);
});
it('resolveTestPath()', () => {
expect(
snapshotResolver.resolveTestPath('/abc/cde/__snapshots__/a.test.js.snap'),
).toBe(path.resolve('/abc/cde/a.test.js'));
});
});
describe('custom resolver in project config', () => {
let snapshotResolver;
const customSnapshotResolverFile = path.join(
__dirname,
'fixtures',
'customSnapshotResolver.js',
);
const projectConfig = {
rootDir: 'custom1',
snapshotResolver: customSnapshotResolverFile,
};
beforeEach(() => {
snapshotResolver = buildSnapshotResolver(projectConfig);
});
it('returns cached object if called multiple times', () => {
expect(buildSnapshotResolver(projectConfig)).toBe(snapshotResolver);
});
it('resolveSnapshotPath()', () => {
expect(
snapshotResolver.resolveSnapshotPath(
path.resolve('/abc/cde/__tests__/a.test.js'),
),
).toBe(path.resolve('/abc/cde/__snapshots__/a.test.js.snap'));
});
it('resolveTestPath()', () => {
expect(
snapshotResolver.resolveTestPath(
path.resolve('/abc', 'cde', '__snapshots__', 'a.test.js.snap'),
),
).toBe(path.resolve('/abc/cde/__tests__/a.test.js'));
});
});
describe('malformed custom resolver in project config', () => {
const newProjectConfig = (filename: string) => {
const customSnapshotResolverFile = path.join(
__dirname,
'fixtures',
filename,
);
return {
rootDir: 'missing-resolveSnapshotPath',
snapshotResolver: customSnapshotResolverFile,
};
};
it('missing resolveSnapshotPath throws ', () => {
const projectConfig = newProjectConfig(
'customSnapshotResolver-missing-resolveSnapshotPath.js',
);
expect(() => {
buildSnapshotResolver(projectConfig);
}).toThrowErrorMatchingSnapshot();
});
it('missing resolveTestPath throws ', () => {
const projectConfig = newProjectConfig(
'customSnapshotResolver-missing-resolveTestPath.js',
);
expect(() => {
buildSnapshotResolver(projectConfig);
}).toThrowErrorMatchingSnapshot();
});
it('inconsistent functions throws ', () => {
const projectConfig = newProjectConfig(
'customSnapshotResolver-inconsistent-fns.js',
);
expect(() => {
buildSnapshotResolver(projectConfig);
}).toThrowErrorMatchingSnapshot();
});
});

View File

@ -13,7 +13,6 @@ const chalk = require('chalk');
const {
getSnapshotData,
getSnapshotPath,
keyToTestName,
saveSnapshotFile,
serialize,
@ -51,12 +50,6 @@ test('testNameToKey', () => {
expect(testNameToKey('abc cde ', 12)).toBe('abc cde 12');
});
test('getSnapshotPath()', () => {
expect(getSnapshotPath('/abc/cde/a.test.js')).toBe(
path.join('/abc', 'cde', '__snapshots__', 'a.test.js.snap'),
);
});
test('saveSnapshotFile() works with \r\n', () => {
const filename = path.join(__dirname, 'remove-newlines.snap');
const data = {

View File

@ -10,11 +10,16 @@
import type {HasteFS} from 'types/HasteMap';
import type {MatcherState} from 'types/Matchers';
import type {Path, SnapshotUpdateState} from 'types/Config';
import type {SnapshotResolver} from 'types/SnapshotResolver';
import fs from 'fs';
import path from 'path';
import diff from 'jest-diff';
import {EXPECTED_COLOR, matcherHint, RECEIVED_COLOR} from 'jest-matcher-utils';
import {
buildSnapshotResolver,
isSnapshotPath,
EXTENSION,
} from './snapshot_resolver';
import SnapshotState from './State';
import {addSerializer, getSerializers} from './plugins';
import * as utils from './utils';
@ -22,20 +27,17 @@ import * as utils from './utils';
const fileExists = (filePath: Path, hasteFS: HasteFS): boolean =>
hasteFS.exists(filePath) || fs.existsSync(filePath);
const cleanup = (hasteFS: HasteFS, update: SnapshotUpdateState) => {
const pattern = '\\.' + utils.SNAPSHOT_EXTENSION + '$';
const cleanup = (
hasteFS: HasteFS,
update: SnapshotUpdateState,
snapshotResolver: SnapshotResolver,
) => {
const pattern = '\\.' + EXTENSION + '$';
const files = hasteFS.matchFiles(pattern);
const filesRemoved = files
.filter(
snapshotFile =>
!fileExists(
path.resolve(
path.dirname(snapshotFile),
'..',
path.basename(snapshotFile, '.' + utils.SNAPSHOT_EXTENSION),
),
hasteFS,
),
!fileExists(snapshotResolver.resolveTestPath(snapshotFile), hasteFS),
)
.map(snapshotFile => {
if (update === 'all') {
@ -290,11 +292,13 @@ const _toThrowErrorMatchingSnapshot = ({
};
module.exports = {
EXTENSION: utils.SNAPSHOT_EXTENSION,
EXTENSION,
SnapshotState,
addSerializer,
buildSnapshotResolver,
cleanup,
getSerializers,
isSnapshotPath,
toMatchInlineSnapshot,
toMatchSnapshot,
toThrowErrorMatchingInlineSnapshot,

View File

@ -0,0 +1,91 @@
import type {ProjectConfig, Path} from 'types/Config';
import type {SnapshotResolver} from 'types/SnapshotResolver';
import chalk from 'chalk';
import path from 'path';
export const EXTENSION = 'snap';
export const DOT_EXTENSION = '.' + EXTENSION;
export const isSnapshotPath = (path: string): boolean =>
path.endsWith(DOT_EXTENSION);
const cache: Map<Path, SnapshotResolver> = new Map();
export const buildSnapshotResolver = (
config: ProjectConfig,
): SnapshotResolver => {
const key = config.rootDir;
if (!cache.has(key)) {
cache.set(key, createSnapshotResolver(config.snapshotResolver));
}
return cache.get(key);
};
function createSnapshotResolver(snapshotResolverPath: ?Path): SnapshotResolver {
return typeof snapshotResolverPath === 'string'
? createCustomSnapshotResolver(snapshotResolverPath)
: {
resolveSnapshotPath: (testPath: Path) =>
path.join(
path.join(path.dirname(testPath), '__snapshots__'),
path.basename(testPath) + DOT_EXTENSION,
),
resolveTestPath: (snapshotPath: Path) =>
path.resolve(
path.dirname(snapshotPath),
'..',
path.basename(snapshotPath, DOT_EXTENSION),
),
};
}
function createCustomSnapshotResolver(
snapshotResolverPath: Path,
): SnapshotResolver {
const custom = (require(snapshotResolverPath): SnapshotResolver);
if (typeof custom.resolveSnapshotPath !== 'function') {
throw new TypeError(mustImplement('resolveSnapshotPath'));
}
if (typeof custom.resolveTestPath !== 'function') {
throw new TypeError(mustImplement('resolveTestPath'));
}
const customResolver = {
resolveSnapshotPath: testPath =>
custom.resolveSnapshotPath(testPath, DOT_EXTENSION),
resolveTestPath: snapshotPath =>
custom.resolveTestPath(snapshotPath, DOT_EXTENSION),
};
verifyConsistentTransformations(customResolver);
return customResolver;
}
function mustImplement(functionName: string) {
return (
chalk.bold(
`Custom snapshot resolver must implement a \`${functionName}\` function.`,
) +
'\nDocumentation: https://facebook.github.io/jest/docs/en/configuration.html#snapshotResolver'
);
}
function verifyConsistentTransformations(custom: SnapshotResolver) {
const fakeTestPath = path.posix.join(
'some-path',
'__tests__',
'snapshot_resolver.test.js',
);
const transformedPath = custom.resolveTestPath(
custom.resolveSnapshotPath(fakeTestPath),
);
if (transformedPath !== fakeTestPath) {
throw new Error(
chalk.bold(
`Custom snapshot resolver functions must transform paths consistently, i.e. expects resolveTestPath(resolveSnapshotPath('${fakeTestPath}')) === ${transformedPath}`,
),
);
}
}

View File

@ -17,7 +17,6 @@ import naturalCompare from 'natural-compare';
import path from 'path';
import prettyFormat from 'pretty-format';
export const SNAPSHOT_EXTENSION = 'snap';
export const SNAPSHOT_VERSION = '1';
const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/;
export const SNAPSHOT_GUIDE_LINK = 'https://goo.gl/fbAQLP';
@ -92,12 +91,6 @@ export const keyToTestName = (key: string) => {
return key.replace(/ \d+$/, '');
};
export const getSnapshotPath = (testPath: Path) =>
path.join(
path.join(path.dirname(testPath), '__snapshots__'),
path.basename(testPath) + '.' + SNAPSHOT_EXTENSION,
);
export const getSnapshotData = (
snapshotPath: Path,
update: SnapshotUpdateState,

View File

@ -153,6 +153,7 @@ export type InitialOptions = {
silent?: boolean,
skipFilter?: boolean,
skipNodeResolution?: boolean,
snapshotResolver?: Path,
snapshotSerializers?: Array<Path>,
errorOnDeprecated?: boolean,
testEnvironment?: string,
@ -273,6 +274,7 @@ export type ProjectConfig = {|
setupTestFrameworkScriptFile: ?Path,
skipFilter: boolean,
skipNodeResolution: boolean,
snapshotResolver: ?Path,
snapshotSerializers: Array<Path>,
testEnvironment: string,
testEnvironmentOptions: Object,

View File

@ -0,0 +1,6 @@
import type {Path} from './Config';
export type SnapshotResolver = {|
resolveSnapshotPath(testPath: Path): Path,
resoveTestPath(snapshotPath: Path): Path,
|};