Skip to content

Commit 01019d8

Browse files
feat: Allow custom serializers through addSerializers
Provides new addSerializers function to add additional serializer objects to be used before diffing. React component serializer implementation using `react-test-renderer` has been extracted out. Fixes #18 Fixes #30
1 parent 8cc8f70 commit 01019d8

File tree

7 files changed

+763
-44
lines changed

7 files changed

+763
-44
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`can use contextLines with enzyme shallow rendered components 1`] = `
4+
"Snapshot Diff:
5+
- First value
6+
+ Second value
7+
8+
@@ -6,1 +6,1 @@
9+
- say
10+
+ my name
11+
@@ -9,1 +9,1 @@
12+
- say
13+
+ my name
14+
@@ -32,1 +32,1 @@
15+
- say
16+
+ my name"
17+
`;
18+
19+
exports[`detects enzyme shallow rendered components 1`] = `
20+
"Snapshot Diff:
21+
- First value
22+
+ Second value
23+
24+
@@ -1,14 +1,14 @@
25+
<div>
26+
<span />
27+
<span />
28+
<span />
29+
<span>
30+
- say
31+
+ my name
32+
</span>
33+
<span>
34+
- say
35+
+ my name
36+
</span>
37+
<span />
38+
<span />
39+
<span />
40+
<span />
41+
@@ -27,11 +27,11 @@
42+
<span />
43+
<span />
44+
<span />
45+
<span />
46+
<span>
47+
- say
48+
+ my name
49+
</span>
50+
<span />
51+
<span />
52+
<span />
53+
<span />"
54+
`;

__tests__/addSerializers.test.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// @flow
2+
const React = require('react');
3+
const { configure, shallow } = require('enzyme');
4+
const Adapter = require('enzyme-adapter-react-16');
5+
const enzymeToJson = require('enzyme-to-json/serializer');
6+
const snapshotDiff = require('../src/index');
7+
8+
configure({ adapter: new Adapter() });
9+
snapshotDiff.addSerializers([enzymeToJson]);
10+
11+
type Props = {
12+
test: string,
13+
};
14+
15+
class Component extends React.Component<Props> {
16+
render() {
17+
return (
18+
<div>
19+
<span />
20+
<span />
21+
<span />
22+
<span>{this.props.test}</span>
23+
<span>{this.props.test}</span>
24+
<span />
25+
<span />
26+
<span />
27+
<span />
28+
<span />
29+
<span />
30+
<span />
31+
<span />
32+
<span />
33+
<span />
34+
<span />
35+
<span />
36+
<span />
37+
<span />
38+
<span />
39+
<span />
40+
<span />
41+
<span />
42+
<span />
43+
<span />
44+
<span>{this.props.test}</span>
45+
<span />
46+
<span />
47+
<span />
48+
<span />
49+
<span />
50+
<span />
51+
<span />
52+
<span />
53+
<span />
54+
<span />
55+
<span />
56+
<span />
57+
<span />
58+
<span />
59+
<span />
60+
<span />
61+
<span />
62+
<span />
63+
<span />
64+
<span />
65+
<span />
66+
<span />
67+
<span />
68+
<span />
69+
<span />
70+
<span />
71+
<span />
72+
<span />
73+
<span />
74+
<span />
75+
<span />
76+
<span />
77+
<span />
78+
<span />
79+
<span />
80+
<span />
81+
<span />
82+
<span />
83+
<span />
84+
<span />
85+
<span />
86+
<span />
87+
<span />
88+
<span />
89+
</div>
90+
);
91+
}
92+
}
93+
94+
test('detects enzyme shallow rendered components', () => {
95+
expect(
96+
snapshotDiff(
97+
shallow(<Component test="say" />),
98+
shallow(<Component test="my name" />)
99+
)
100+
).toMatchSnapshot();
101+
});
102+
103+
test('can use contextLines with enzyme shallow rendered components', () => {
104+
expect(
105+
snapshotDiff(
106+
shallow(<Component test="say" />),
107+
shallow(<Component test="my name" />),
108+
{
109+
contextLines: 0,
110+
}
111+
)
112+
).toMatchSnapshot();
113+
});

extend-expect.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* global expect */
12
const { toMatchDiffSnapshot } = require('./build/');
23

3-
expect.extend({ toMatchDiffSnapshot });
4+
expect.extend({ toMatchDiffSnapshot });

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@
3030
"@babel/preset-env": "^7.0.0",
3131
"@babel/preset-flow": "^7.0.0",
3232
"@babel/preset-react": "^7.0.0",
33+
"enzyme": "^3.10.0",
34+
"enzyme-adapter-react-16": "^1.14.0",
35+
"enzyme-to-json": "^3.4.0",
3336
"eslint": "^5.15.3",
3437
"eslint-config-callstack-io": "^1.1.1",
3538
"flow-bin": "^0.102.0",
3639
"jest": "^24.0.0",
3740
"react": "^16.7.0",
41+
"react-dom": "16.7.0",
3842
"react-test-renderer": "^16.7.0"
3943
}
4044
}

src/index.js

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44

55
const diff = require('jest-diff');
66
const snapshot = require('jest-snapshot');
7-
const prettyFormat = require('pretty-format');
8-
9-
const { ReactElement } = prettyFormat.plugins;
10-
const reactElement = Symbol.for('react.element');
7+
const reactSerializer = require('./react-serializer');
118

129
type Options = {|
1310
expand?: boolean,
@@ -29,12 +26,28 @@ const defaultOptions = {
2926

3027
const SNAPSHOT_TITLE = 'Snapshot Diff:\n';
3128

29+
const identity = value => value;
30+
let serializers = [reactSerializer];
31+
3232
const snapshotDiff = (valueA: any, valueB: any, options?: Options): string => {
3333
let difference;
3434
const mergedOptions = { ...defaultOptions, ...options };
3535

36-
if (isReactComponent(valueA) && isReactComponent(valueB)) {
37-
difference = diffReactComponents(valueA, valueB, mergedOptions);
36+
const matchingSerializer = serializers.find(
37+
({ test }) => test(valueA) && test(valueB)
38+
);
39+
40+
if (matchingSerializer) {
41+
const { print, diffOptions } = matchingSerializer;
42+
const serializerOptions = diffOptions
43+
? diffOptions(valueA, valueB) || {}
44+
: {};
45+
const combinedOptions = { ...mergedOptions, ...serializerOptions };
46+
difference = diffStrings(
47+
print(valueA, identity),
48+
print(valueB, identity),
49+
combinedOptions
50+
);
3851
} else {
3952
difference = diffStrings(valueA, valueB, mergedOptions);
4053
}
@@ -55,9 +68,6 @@ const snapshotDiff = (valueA: any, valueB: any, options?: Options): string => {
5568
return SNAPSHOT_TITLE + difference;
5669
};
5770

58-
const isReactComponent = (value: any) =>
59-
value && value.$$typeof === reactElement;
60-
6171
function diffStrings(valueA: any, valueB: any, options: Options) {
6272
return diff(valueA, valueB, {
6373
expand: options.expand,
@@ -67,36 +77,6 @@ function diffStrings(valueA: any, valueB: any, options: Options) {
6777
});
6878
}
6979

70-
function requireReactTestRenderer() {
71-
try {
72-
return require('react-test-renderer'); // eslint-disable-line import/no-extraneous-dependencies
73-
} catch (error) {
74-
if (error.code === 'MODULE_NOT_FOUND') {
75-
throw new Error(
76-
`Failed to load optional module "react-test-renderer". ` +
77-
`If you need to compare React elements, please add "react-test-renderer" to your ` +
78-
`project's dependencies.\n` +
79-
`${error.message}`
80-
);
81-
}
82-
throw error;
83-
}
84-
}
85-
86-
function diffReactComponents(valueA: any, valueB: any, options: Options) {
87-
const renderer = requireReactTestRenderer();
88-
const reactValueA = renderer.create(valueA).toJSON();
89-
const reactValueB = renderer.create(valueB).toJSON();
90-
const prettyFormatOptions = { plugins: [ReactElement], min: true };
91-
92-
return diff(reactValueA, reactValueB, {
93-
expand: options.expand,
94-
contextLines: options.contextLines,
95-
aAnnotation: prettyFormat(valueA, prettyFormatOptions),
96-
bAnnotation: prettyFormat(valueB, prettyFormatOptions),
97-
});
98-
}
99-
10080
function toMatchDiffSnapshot(
10181
valueA: any,
10282
valueB: any,
@@ -119,7 +99,12 @@ function getSnapshotDiffSerializer() {
11999
};
120100
}
121101

102+
function addSerializers(customSerializers) {
103+
serializers = [reactSerializer].concat(customSerializers);
104+
}
105+
122106
module.exports = snapshotDiff;
123107
module.exports.snapshotDiff = snapshotDiff;
124108
module.exports.toMatchDiffSnapshot = toMatchDiffSnapshot;
125109
module.exports.getSnapshotDiffSerializer = getSnapshotDiffSerializer;
110+
module.exports.addSerializers = addSerializers;

src/react-serializer.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// @flow
2+
3+
'use strict';
4+
5+
const prettyFormat = require('pretty-format');
6+
7+
const { ReactElement } = prettyFormat.plugins;
8+
const reactElement = Symbol.for('react.element');
9+
10+
function getReactComponentSerializer() {
11+
let renderer;
12+
try {
13+
renderer = require('react-test-renderer'); // eslint-disable-line import/no-extraneous-dependencies
14+
} catch (error) {
15+
if (error.code === 'MODULE_NOT_FOUND') {
16+
throw new Error(
17+
`Failed to load optional module "react-test-renderer". ` +
18+
`If you need to compare React elements, please add "react-test-renderer" to your ` +
19+
`project's dependencies.\n` +
20+
`${error.message}`
21+
);
22+
}
23+
throw error;
24+
}
25+
return value => renderer.create(value).toJSON();
26+
}
27+
28+
const reactSerializer = {
29+
test: (value: any) => value && value.$$typeof === reactElement,
30+
print: (value: any) => {
31+
const reactComponentSerializer = getReactComponentSerializer();
32+
return reactComponentSerializer(value);
33+
},
34+
diffOptions: (valueA: any, valueB: any) => {
35+
const prettyFormatOptions = { plugins: [ReactElement], min: true };
36+
return {
37+
aAnnotation: prettyFormat(valueA, prettyFormatOptions),
38+
bAnnotation: prettyFormat(valueB, prettyFormatOptions),
39+
};
40+
},
41+
};
42+
43+
module.exports = reactSerializer;

0 commit comments

Comments
 (0)