Skip to content

Commit

Permalink
Indented inline snapshots. (#8198)
Browse files Browse the repository at this point in the history
* Indent inline snapshots.

* Typo.

* Extra assertions on assumptions.

* Resolve lint errors.

* More accurate comment.

* Update CHANGELOG.md

* Review feedback.

* Update packages/jest-snapshot/src/inline_snapshots.ts

Co-Authored-By: scotthovestadt <scotthovestadt@users.noreply.github.com>

* Review feedback.
  • Loading branch information
scotthovestadt committed Mar 24, 2019
1 parent e75eaf0 commit 5cde98e
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 49 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,7 @@
- `[@jest/reporter]` Display todo and skip test descriptions when verbose is true ([#8038](https://github.com/facebook/jest/pull/8038))
- `[jest-runner]` Support default exports for test environments ([#8163](https://github.com/facebook/jest/pull/8163))
- `[pretty-format]` Support React.Suspense ([#8180](https://github.com/facebook/jest/pull/8180))
- `[jest-snapshot]` Indent inline snapshots ([#8198](https://github.com/facebook/jest/pull/8198))

### Fixes

Expand Down
72 changes: 36 additions & 36 deletions e2e/__tests__/__snapshots__/toMatchInlineSnapshot.test.ts.snap
Expand Up @@ -3,40 +3,40 @@
exports[`basic support: initial write 1`] = `
test('inline snapshots', () =>
expect({apple: 'original value'}).toMatchInlineSnapshot(\`
Object {
"apple": "original value",
}
\`));
Object {
"apple": "original value",
}
\`));
`;

exports[`basic support: snapshot mismatch 1`] = `
test('inline snapshots', () =>
expect({apple: 'updated value'}).toMatchInlineSnapshot(\`
Object {
"apple": "original value",
}
\`));
Object {
"apple": "original value",
}
\`));
`;

exports[`basic support: snapshot passed 1`] = `
test('inline snapshots', () =>
expect({apple: 'original value'}).toMatchInlineSnapshot(\`
Object {
"apple": "original value",
}
\`));
Object {
"apple": "original value",
}
\`));
`;

exports[`basic support: snapshot updated 1`] = `
test('inline snapshots', () =>
expect({apple: 'updated value'}).toMatchInlineSnapshot(\`
Object {
"apple": "updated value",
}
\`));
Object {
"apple": "updated value",
}
\`));
`;

Expand All @@ -45,10 +45,10 @@ test('handles property matchers', () => {
expect({createdAt: new Date()}).toMatchInlineSnapshot(
{createdAt: expect.any(Date)},
\`
Object {
"createdAt": Any<Date>,
}
\`
Object {
"createdAt": Any<Date>,
}
\`
);
});
Expand All @@ -59,10 +59,10 @@ test('handles property matchers', () => {
expect({createdAt: "string"}).toMatchInlineSnapshot(
{createdAt: expect.any(Date)},
\`
Object {
"createdAt": Any<Date>,
}
\`
Object {
"createdAt": Any<Date>,
}
\`
);
});
Expand All @@ -73,10 +73,10 @@ test('handles property matchers', () => {
expect({createdAt: new Date()}).toMatchInlineSnapshot(
{createdAt: expect.any(Date)},
\`
Object {
"createdAt": Any<Date>,
}
\`
Object {
"createdAt": Any<Date>,
}
\`
);
});
Expand All @@ -87,10 +87,10 @@ test('handles property matchers', () => {
expect({createdAt: 'string'}).toMatchInlineSnapshot(
{createdAt: expect.any(String)},
\`
Object {
"createdAt": Any<String>,
}
\`
Object {
"createdAt": Any<String>,
}
\`
);
});
Expand Down Expand Up @@ -139,10 +139,10 @@ test('inline snapshots', async () => {
exports[`writes snapshots with non-literals in expect(...) 1`] = `
it('works with inline snapshots', () => {
expect({a: 1}).toMatchInlineSnapshot(\`
Object {
"a": 1,
}
\`);
Object {
"a": 1,
}
\`);
});
`;
105 changes: 105 additions & 0 deletions packages/jest-snapshot/src/__tests__/inline_snapshots.test.ts
Expand Up @@ -199,3 +199,108 @@ test('saveInlineSnapshots() works with non-literals in expect call', () => {
"expect({a: 'a'}).toMatchInlineSnapshot(`{a: 'a'}`);\n",
);
});

test('saveInlineSnapshots() indents multi-line snapshots with spaces', () => {
const filename = path.join(__dirname, 'my.test.js');
(fs.readFileSync as jest.Mock).mockImplementation(
() =>
"it('is a test', () => {\n" +
" expect({a: 'a'}).toMatchInlineSnapshot();\n" +
'});\n',
);
(prettier.resolveConfig.sync as jest.Mock).mockReturnValue({
bracketSpacing: false,
singleQuote: true,
});

saveInlineSnapshots(
[
{
frame: {column: 20, file: filename, line: 2} as Frame,
snapshot: `\nObject {\n a: 'a'\n}\n`,
},
],
prettier,
babelTraverse,
);

expect(fs.writeFileSync).toHaveBeenCalledWith(
filename,
"it('is a test', () => {\n" +
" expect({a: 'a'}).toMatchInlineSnapshot(`\n" +
' Object {\n' +
" a: 'a'\n" +
' }\n' +
' `);\n' +
'});\n',
);
});

test('saveInlineSnapshots() indents multi-line snapshots with tabs', () => {
const filename = path.join(__dirname, 'my.test.js');
(fs.readFileSync as jest.Mock).mockImplementation(
() =>
"it('is a test', () => {\n" +
" expect({a: 'a'}).toMatchInlineSnapshot();\n" +
'});\n',
);
(prettier.resolveConfig.sync as jest.Mock).mockReturnValue({
bracketSpacing: false,
singleQuote: true,
useTabs: true,
});

saveInlineSnapshots(
[
{
frame: {column: 20, file: filename, line: 2} as Frame,
snapshot: `\nObject {\n a: 'a'\n}\n`,
},
],
prettier,
babelTraverse,
);

expect(fs.writeFileSync).toHaveBeenCalledWith(
filename,
"it('is a test', () => {\n" +
"\texpect({a: 'a'}).toMatchInlineSnapshot(`\n" +
'\t\tObject {\n' +
"\t\t a: 'a'\n" +
'\t\t}\n' +
'\t`);\n' +
'});\n',
);
});

test('saveInlineSnapshots() indents snapshots after prettier reformats', () => {
const filename = path.join(__dirname, 'my.test.js');
(fs.readFileSync as jest.Mock).mockImplementation(
() => "it('is a test', () => expect({a: 'a'}).toMatchInlineSnapshot());\n",
);
(prettier.resolveConfig.sync as jest.Mock).mockReturnValue({
bracketSpacing: false,
singleQuote: true,
});

saveInlineSnapshots(
[
{
frame: {column: 40, file: filename, line: 1} as Frame,
snapshot: `\nObject {\n a: 'a'\n}\n`,
},
],
prettier,
babelTraverse,
);

expect(fs.writeFileSync).toHaveBeenCalledWith(
filename,
"it('is a test', () =>\n" +
" expect({a: 'a'}).toMatchInlineSnapshot(`\n" +
' Object {\n' +
" a: 'a'\n" +
' }\n' +
' `));\n',
);
});
42 changes: 41 additions & 1 deletion packages/jest-snapshot/src/index.ts
Expand Up @@ -51,6 +51,7 @@ const NOT_SNAPSHOT_MATCHERS = `.${BOLD_WEIGHT(
const HINT_ARG = BOLD_WEIGHT('hint');
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.
Expand All @@ -73,6 +74,45 @@ const printName = (
);
};

function stripAddedIndentation(inlineSnapshot: string) {
// 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].indexOf(indentation) !== 0) {
// All lines except first and last should have the same indent as the
// first line (or more). If this isn't the case we don't want to touch it.
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: Config.Path, hasteFS: HasteFS): boolean =>
hasteFS.exists(filePath) || fs.existsSync(filePath);

Expand Down Expand Up @@ -182,7 +222,7 @@ const toMatchInlineSnapshot = function(
return _toMatchSnapshot({
context: this,
expectedArgument,
inlineSnapshot: inlineSnapshot || '',
inlineSnapshot: stripAddedIndentation(inlineSnapshot || ''),
matcherName,
options,
propertyMatchers,
Expand Down

0 comments on commit 5cde98e

Please sign in to comment.