Skip to content

Commit

Permalink
Update pretty-format and use it for diffs; update snapshot printing; …
Browse files Browse the repository at this point in the history
…print without toJSON if objects appear similar. (jestjs#1752)
  • Loading branch information
cpojer authored Sep 23, 2016
1 parent 596f228 commit 0a94759
Show file tree
Hide file tree
Showing 21 changed files with 693 additions and 458 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,17 @@ exports[`test renders the ListView component 1`] = `
}
}
initialListSize={10}
onContentSizeChange={[Function bound _onContentSizeChange]}
onContentSizeChange={[Function]}
onEndReachedThreshold={1000}
onKeyboardDidHide={undefined}
onKeyboardDidShow={undefined}
onKeyboardWillHide={undefined}
onKeyboardWillShow={undefined}
onLayout={[Function bound _onLayout]}
onScroll={[Function bound _onScroll]}
onLayout={[Function]}
onScroll={[Function]}
pageSize={1}
removeClippedSubviews={true}
renderRow={[Function renderRow]}
renderRow={[Function]}
scrollEventThrottle={50}
scrollRenderAheadDistance={1000}
stickyHeaderIndices={Array []}>
Expand Down
24 changes: 12 additions & 12 deletions examples/snapshot/__tests__/__snapshots__/Link.react-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ exports[`test changes the class when hovered 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function bound _onMouseEnter]}
onMouseLeave={[Function bound _onMouseLeave]}>
onMouseEnter={[Function]}
onMouseLeave={[Function]}>
Facebook
</a>
`;
Expand All @@ -12,8 +12,8 @@ exports[`test changes the class when hovered 2`] = `
<a
className="hovered"
href="http://www.facebook.com"
onMouseEnter={[Function bound _onMouseEnter]}
onMouseLeave={[Function bound _onMouseLeave]}>
onMouseEnter={[Function]}
onMouseLeave={[Function]}>
Facebook
</a>
`;
Expand All @@ -22,8 +22,8 @@ exports[`test changes the class when hovered 3`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function bound _onMouseEnter]}
onMouseLeave={[Function bound _onMouseLeave]}>
onMouseEnter={[Function]}
onMouseLeave={[Function]}>
Facebook
</a>
`;
Expand All @@ -32,8 +32,8 @@ exports[`test properly escapes quotes 1`] = `
<a
className="normal"
href="#"
onMouseEnter={[Function bound _onMouseEnter]}
onMouseLeave={[Function bound _onMouseLeave]}>
onMouseEnter={[Function]}
onMouseLeave={[Function]}>
\"Facebook\" \\\'is \\ \'awesome\'
</a>
`;
Expand All @@ -42,8 +42,8 @@ exports[`test renders as an anchor when no page is set 1`] = `
<a
className="normal"
href="#"
onMouseEnter={[Function bound _onMouseEnter]}
onMouseLeave={[Function bound _onMouseLeave]}>
onMouseEnter={[Function]}
onMouseLeave={[Function]}>
Facebook
</a>
`;
Expand All @@ -52,8 +52,8 @@ exports[`test renders correctly 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function bound _onMouseEnter]}
onMouseLeave={[Function bound _onMouseLeave]}>
onMouseEnter={[Function]}
onMouseLeave={[Function]}>
Facebook
</a>
`;
2 changes: 1 addition & 1 deletion packages/jest-diff/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"chalk": "^1.1.3",
"diff": "^3.0.0",
"jest-matcher-utils": "^15.1.0",
"pretty-format": "~4.0.0"
"pretty-format": "~4.2.1"
},
"scripts": {
"test": "../../packages/jest-cli/bin/jest.js"
Expand Down
15 changes: 15 additions & 0 deletions packages/jest-diff/src/__tests__/__snapshots__/diff-test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
exports[`test falls back to not call toJSON if objects look identical 1`] = `
"Compared values serialize to the same structure.
Printing internal object structure without calling \`toJSON\` instead.
- Expected
+ Received
  Object {
-  \"line\": 1,
+  \"line\": 2,
   \"toJSON\": [Function toJSON],
  }"
`;
exports[`test prints a fallback message if two objects truly look identical 1`] = `"Compared values have no visual difference."`;
18 changes: 17 additions & 1 deletion packages/jest-diff/src/__tests__/diff-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
const diff = require('../');
const stripAnsi = require('strip-ansi');

const toJSON = function toJSON() {
return 'apple';
};

describe('different types', () => {
[
[1, 'a', 'number', 'string'],
Expand Down Expand Up @@ -54,7 +58,7 @@ describe('no visual difference', () => {
`'${JSON.stringify(values[0])}' and '${JSON.stringify(values[1])}'`,
() => {
expect(stripAnsi(diff(values[0], values[1]))).toBe(
'Compared values have no visual difference',
'Compared values have no visual difference.',
);
},
);
Expand All @@ -68,6 +72,18 @@ test('oneline strings', () => {
expect(stripAnsi(diff('123456789', '234567890'))).toBe(null);
});

test('falls back to not call toJSON if objects look identical', () => {
const a = {toJSON, line: 1};
const b = {toJSON, line: 2};
expect(diff(a, b)).toMatchSnapshot();
});

test('prints a fallback message if two objects truly look identical', () => {
const a = {toJSON, line: 2};
const b = {toJSON, line: 2};
expect(diff(a, b)).toMatchSnapshot();
});

test('multiline strings', () => {
const result = diff(
`line 1
Expand Down
10 changes: 8 additions & 2 deletions packages/jest-diff/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@

const chalk = require('chalk');

module.exports.NO_DIFF_MESSAGE =
chalk.dim.underline('Compared values have no visual difference');
exports.NO_DIFF_MESSAGE =
chalk.dim('Compared values have no visual difference.');

exports.SIMILAR_MESSAGE =
chalk.dim(
'Compared values serialize to the same structure.\n' +
'Printing internal object structure without calling `toJSON` instead.',
);
2 changes: 1 addition & 1 deletion packages/jest-diff/src/diffStrings.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const getAnnotation = options =>
chalk.red('+ ' + ((options && options.bAnnotation) || 'Received')) + '\n\n';

// diff characters if oneliner and diff lines if multiline
function diffStrings(a: string, b: string, options: ?DiffOptions): ?string {
function diffStrings(a: string, b: string, options: ?DiffOptions): string {
let isDifferent = false;

// `diff` uses the Myers LCS diff algorithm which runs in O(n+d^2) time
Expand Down
45 changes: 39 additions & 6 deletions packages/jest-diff/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,19 @@ const diffStrings = require('./diffStrings');
const {getType} = require('jest-matcher-utils');
const prettyFormat = require('pretty-format');

const jsxLikePlugins = [ReactTestComponentPlugin, ReactElementPlugin];
const NO_DIFF_MESSAGE = require('./constants').NO_DIFF_MESSAGE;
const {
NO_DIFF_MESSAGE,
SIMILAR_MESSAGE,
} = require('./constants');

const PLUGINS = [ReactTestComponentPlugin, ReactElementPlugin];
const FORMAT_OPTIONS = {
plugins: PLUGINS,
};
const FALLBACK_FORMAT_OPTIONS = {
callToJSON: false,
plugins: PLUGINS,
};

// Generate a string that will highlight the difference between two values
// with green and red. (similar to how github does code diffing)
Expand Down Expand Up @@ -49,10 +60,32 @@ function diff(a: any, b: any, options: ?DiffOptions): ?string {
case 'boolean':
return null;
default:
return diffStrings(
prettyFormat(a, {plugins: jsxLikePlugins}, options),
prettyFormat(b, {plugins: jsxLikePlugins}, options),
);
let diffMessage;
let hasThrown = false;

try {
diffMessage = diffStrings(
prettyFormat(a, FORMAT_OPTIONS),
prettyFormat(b, FORMAT_OPTIONS),
options,
);
} catch (e) {
hasThrown = true;
}

// If the comparison yields no results, compare again but this time
// without calling `toJSON`. It's also possible that toJSON might throw.
if (!diffMessage || diffMessage === NO_DIFF_MESSAGE) {
diffMessage = diffStrings(
prettyFormat(a, FALLBACK_FORMAT_OPTIONS),
prettyFormat(b, FALLBACK_FORMAT_OPTIONS),
options,
);
if (diffMessage !== NO_DIFF_MESSAGE && !hasThrown) {
diffMessage = SIMILAR_MESSAGE + '\n\n' + diffMessage;
}
}
return diffMessage;
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/jest-matcher-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"test": "../../packages/jest-cli/bin/jest.js"
},
"dependencies": {
"chalk": "^1.1.3"
"chalk": "^1.1.3",
"pretty-format": "~4.2.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
exports[`.stringify() toJSON errors when comparing two objects 1`] = `
"expect(received).toEqual(expected)
Expected value to equal:
{\"b\": 1, \"toJSON\": [Function toJSON]}
Received:
{\"a\": 1, \"toJSON\": [Function toJSON]}
Difference:
- Expected
+ Received
  Object {
-  \"b\": 1,
+  \"a\": 1,
   \"toJSON\": [Function toJSON],
  }"
`;
48 changes: 36 additions & 12 deletions packages/jest-matcher-utils/src/__tests__/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ const {stringify, getType} = require('../');

describe('.stringify()', () => {
[
[[], '[]'],
[[], 'Array []'],
[{}, '{}'],
[1, '1'],
[0, '0'],
[1.5, '1.5'],
[null, 'null'],
[undefined, '"undefined"'],
[undefined, 'undefined'],
['abc', '"abc"'],
[Symbol.for('abc'), '"Symbol(abc)"'],
[NaN, '"NaN"'],
[Infinity, '"Infinity"'],
[-Infinity, '"-Infinity"'],
[/ab\.c/gi, '"/ab\\\\.c/gi"'],
[Symbol.for('abc'), 'Symbol(abc)'],
[NaN, 'NaN'],
[Infinity, 'Infinity'],
[-Infinity, '-Infinity'],
[/ab\.c/gi, '/ab\\.c/gi'],
].forEach(([v, s]) => {
test(stringify(v), () => {
expect(stringify(v)).toBe(s);
Expand All @@ -36,7 +36,7 @@ describe('.stringify()', () => {
test('circular references', () => {
const a = {};
a.a = a;
expect(stringify(a)).toBe('{"a":"[Circular]"}');
expect(stringify(a)).toBe('{"a": [Circular]}');
});

test('toJSON error', () => {
Expand All @@ -45,12 +45,37 @@ describe('.stringify()', () => {
throw new Error('Nope.');
},
};
expect(stringify(evil)).toBe('[object]');
expect(stringify({a: {b: {evil}}})).toBe('[object]');
expect(stringify(evil)).toBe('{"toJSON": [Function toJSON]}');
expect(stringify({a: {b: {evil}}}))
.toBe('{"a": {"b": {"evil": {"toJSON": [Function toJSON]}}}}');

function Evil() {}
Evil.toJSON = evil.toJSON;
expect(stringify(Evil)).toBe('function Evil() {}');
expect(stringify(Evil)).toBe('[Function Evil]');
});

test('toJSON errors when comparing two objects', () => {
function toJSON() {
throw new Error('Nope.');
}
const evilA = {
a: 1,
toJSON,
};
const evilB = {
b: 1,
toJSON,
};

let error;
try {
expect(evilA).toEqual(evilB);
} catch (e) {
error = e;
}

expect(error).toBeDefined();
expect(error.message).toMatchSnapshot();
});
});

Expand All @@ -66,5 +91,4 @@ describe('.getType()', () => {
test('boolean', () => expect(getType(true)).toBe('boolean'));
test('symbol', () => expect(getType(Symbol.for('a'))).toBe('symbol'));
test('regexp', () => expect(getType(/abc/)).toBe('regexp'));

});
Loading

0 comments on commit 0a94759

Please sign in to comment.