Skip to content

Commit

Permalink
Add custom snapshot serializers (#1741)
Browse files Browse the repository at this point in the history
  • Loading branch information
anilanar authored and ANIL ANAR committed Oct 2, 2016
1 parent 81f67ac commit 00e9295
Show file tree
Hide file tree
Showing 21 changed files with 329 additions and 15 deletions.
58 changes: 58 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ These options let you control Jest's behavior in your `package.json` file. The J
- [`scriptPreprocessor` [string]](#scriptpreprocessor-string)
- [`setupFiles` [array]](#setupfiles-array)
- [`setupTestFrameworkScriptFile` [string]](#setuptestframeworkscriptfile-string)
- [`snapshotSerializers` [array<string>]](#snapshotserializers-array-string)
- [`testEnvironment` [string]](#testenvironment-string)
- [`testPathDirs` [array<string>]](#testpathdirs-array-string)
- [`testPathIgnorePatterns` [array<string>]](#testpathignorepatterns-array-string)
Expand Down Expand Up @@ -298,6 +299,63 @@ The path to a module that runs some code to configure or set up the testing fram

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.

### `snapshotSerializers` [array<string>]
(default: `[]`)

A list of paths to snapshot serializer modules Jest should use for snapshot
testing.

Jest has default serializers for built-in javascript types and for react
elements. See [snapshot test tutorial](/jest/docs/tutorial-react-native.html#snapshot-test) for more information.

Example serializer module:

```js
// my-serializer-module
module.exports = {
test: function(val) {
return val && val.hasOwnProperty('foo');
},
print: function(val, serialize, indent) {
return 'Pretty foo: ' + serialize(val.foo);
}
};
```

`serialize` is a function that serializes a value using existing plugins.

To use `my-serializer-module` as a serializer, configuration would be as
follows:

```json
{
"json": {
"snapshotSerializers": ["<rootDir>/node_modules/my-serializer-module"]
}
}
```

Finally tests would look as follows:

```js
test(() => {
const bar = {
foo: {x: 1, y: 2}
};

expect(foo).toMatchSnapshot();
});
```

Rendered snapshot:

```
Pretty foo: Object {
"x": 1,
"y": 2,
}
```

### `testEnvironment` [string]
(default: `'jsdom'`)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
exports[`Snapshot serializers renders snapshot 1`] = `
Object {
"snapshot serializers works with first plugin 1": "foo: 1",
"snapshot serializers works with nested serializable objects 1": "foo: bar: 2",
"snapshot serializers works with second plugin 1": "bar: 2",
}
`;
44 changes: 44 additions & 0 deletions integration_tests/__tests__/snapshot-serializers-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
'use strict';

const path = require('path');
const {cleanup} = require('../utils');
const runJest = require('../runJest');

const testDir = path.resolve(__dirname, '../snapshot-serializers');
const snapshotsDir = path.resolve(testDir, '__tests__/__snapshots__');
const snapshotPath = path.resolve(snapshotsDir, 'snapshot-test.js.snap');

const runAndAssert = () => {
const result = runJest.json('snapshot-serializers');
const json = result.json;
expect(json.numTotalTests).toBe(3);
expect(json.numPassedTests).toBe(3);
expect(json.numFailedTests).toBe(0);
expect(json.numPendingTests).toBe(0);
expect(result.status).toBe(0);
};

describe('Snapshot serializers', () => {
beforeEach(() => cleanup(snapshotsDir));
afterEach(() => cleanup(snapshotsDir));

it('renders snapshot', () => {
runAndAssert();
const snapshot = require(snapshotPath);
expect(snapshot).toMatchSnapshot();
});

it('compares snapshots correctly', () => {
// run twice, second run compares result with snapshot from first run
runAndAssert();
runAndAssert();
});
});
35 changes: 35 additions & 0 deletions integration_tests/snapshot-serializers/__tests__/snapshot-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails oncall+jsinfra
*/
'use strict';

describe('snapshot serializers', () => {
it('works with first plugin', () => {
const test = {
foo: 1,
};
expect(test).toMatchSnapshot();
});

it('works with second plugin', () => {
const test = {
bar: 2,
};
expect(test).toMatchSnapshot();
});

it('works with nested serializable objects', () => {
const test = {
foo: {
bar: 2,
},
};
expect(test).toMatchSnapshot();
});
});
9 changes: 9 additions & 0 deletions integration_tests/snapshot-serializers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"jest": {
"testEnvironment": "node",
"snapshotSerializers": [
"<rootDir>/plugins/foo",
"<rootDir>/plugins/bar"
]
}
}
2 changes: 2 additions & 0 deletions integration_tests/snapshot-serializers/plugins/bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const {createPlugin} = require('../utils');
module.exports = createPlugin('bar');
2 changes: 2 additions & 0 deletions integration_tests/snapshot-serializers/plugins/foo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const {createPlugin} = require('../utils');
module.exports = createPlugin('foo');
6 changes: 6 additions & 0 deletions integration_tests/snapshot-serializers/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
exports.createPlugin = prop => {
return {
test: val => val && val.hasOwnProperty(prop),
print: (val, serialize) => `${prop}: ${serialize(val[prop])}`,
};
};
23 changes: 15 additions & 8 deletions packages/jest-config/src/__tests__/normalize-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails oncall+jsinfra
*/
'use strict';

Expand Down Expand Up @@ -164,45 +163,53 @@ describe('normalize', () => {
});
});

describe('testPathDirs', () => {
function testPathArray(key) {
it('normalizes all paths relative to rootDir', () => {
const config = normalize({
rootDir: '/root/path/foo',
testPathDirs: [
[key]: [
'bar/baz',
'qux/quux/',
],
}, '/root/path');

expect(config.testPathDirs).toEqual([
expect(config[key]).toEqual([
expectedPathFooBar, expectedPathFooQux,
]);
});

it('does not change absolute paths', () => {
const config = normalize({
rootDir: '/root/path/foo',
testPathDirs: [
[key]: [
'/an/abs/path',
'/another/abs/path',
],
});

expect(config.testPathDirs).toEqual([
expect(config[key]).toEqual([
expectedPathAbs, expectedPathAbsAnother,
]);
});

it('substitutes <rootDir> tokens', () => {
const config = normalize({
rootDir: '/root/path/foo',
testPathDirs: [
[key]: [
'<rootDir>/bar/baz',
],
});

expect(config.testPathDirs).toEqual([expectedPathFooBar]);
expect(config[key]).toEqual([expectedPathFooBar]);
});
}

describe('testPathDirs', () => {
testPathArray('testPathDirs');
});

describe('snapshotSerializers', () => {
testPathArray('snapshotSerializers');
});

describe('scriptPreprocessor', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/jest-config/src/defaults.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2014, Facebook, Inc. All rights reserved.
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
Expand Down Expand Up @@ -46,6 +46,7 @@ module.exports = ({
preset: null,
preprocessorIgnorePatterns: [NODE_MODULES_REGEXP],
resetModules: false,
snapshotSerializers: [],
testEnvironment: 'jest-environment-jsdom',
testPathDirs: ['<rootDir>'],
testPathIgnorePatterns: [NODE_MODULES_REGEXP],
Expand Down
9 changes: 8 additions & 1 deletion packages/jest-config/src/normalize.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/**
* Copyright (c) 2014, Facebook, Inc. All rights reserved.
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/

'use strict';
Expand Down Expand Up @@ -314,6 +315,12 @@ function normalize(config, argv) {
));
break;

case 'snapshotSerializers':
value = config[key].map(filePath => path.resolve(
config.rootDir,
_replaceRootDirTags(config.rootDir, filePath),
));
break;
case 'collectCoverageFrom':
if (!config[key]) {
value = [];
Expand Down
19 changes: 19 additions & 0 deletions packages/jest-jasmine2/src/__mocks__/jest-snapshot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/

const snapshot = jest.genMockFromModule('jest-snapshot');
let plugins = [];

snapshot.addPlugins = p => {
plugins = plugins.concat(p);
};
snapshot.getPlugins = p => plugins;
snapshot.__reset = () => plugins = [];

module.exports = snapshot;
30 changes: 30 additions & 0 deletions packages/jest-jasmine2/src/__tests__/setup-jest-globals-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
'use strict';

describe('addPlugins', () => {
const setup = require('../setup-jest-globals');

beforeEach(() => {
require('jest-snapshot').__reset();
});

const test = serializers => {
const {getPlugins} = require('jest-snapshot');
const config = {
snapshotSerializers: [],
};
setup({config});
expect(getPlugins()).toEqual(config.snapshotSerializers);
};

it('should add plugins from an empty array', () => test([]));
it('should add a single plugin', () => test(['foo']));
it('should add multiple plugins', () => test(['foo', 'bar']));
});
3 changes: 2 additions & 1 deletion packages/jest-jasmine2/src/setup-jest-globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import type {Config, Path} from 'types/Config';

const {getState, setState} = require('jest-matchers');
const {initializeSnapshotState} = require('jest-snapshot');
const {initializeSnapshotState, addPlugins} = require('jest-snapshot');

// Get suppressed errors form jest-matchers that weren't throw during
// test execution and add them to the test result, potentially failing
Expand Down Expand Up @@ -69,6 +69,7 @@ type Options = {
};

module.exports = ({testPath, config}: Options) => {
addPlugins(config.snapshotSerializers);
setState({testPath});
patchJasmine();
const snapshotState
Expand Down
35 changes: 35 additions & 0 deletions packages/jest-snapshot/src/__tests__/plugins-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
'use strict';

beforeEach(() => jest.resetModules());

const testPath = serializers => {
const {addPlugins, getPlugins} = require('../plugins');
const serializerPaths = serializers.map(s =>
require.resolve(`./plugins/${s}`),
);
addPlugins(serializerPaths);
const expected = serializerPaths.map(p => require(p));

const plugins = getPlugins();
expect(plugins.length).toBe(serializers.length + 2);
plugins.splice(0, 2);
expect(plugins).toEqual(expected);
};

it('should get plugins', () => {
const {getPlugins} = require('../plugins');
const plugins = getPlugins();
expect(plugins.length).toBe(2);
});

it('should add plugins from an empty array', () => testPath([]));
it('should add a single plugin path', () => testPath(['foo']));
it('should add multiple plugin paths', () => testPath(['foo', 'bar']));
Loading

0 comments on commit 00e9295

Please sign in to comment.