Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indented inline snapshots. #8198

Merged
merged 9 commits into from Mar 24, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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