Skip to content

Commit 8d2c17c

Browse files
viestatG-RathSimenB
authored
feat: create no-interpolation-in-snapshots rule (#553)
* feat(rules): add no-interpolation-inline-snapshot rule * docs: fix fromat in rule documentation * fix: add chamges requested in review * test: update number of rules * docs: update docs/rules/no-interpolation-inline-snapshot.md Co-authored-by: Gareth Jones <[email protected]> * feat: add toThrowErrorMatchingInlineSnapshot * docs: update rules table in README * docs: apply suggestions from code review Co-authored-by: Gareth Jones <[email protected]> * docs: fix format in README * fix: add review suggestions * docs: add examples to README * chore: rename rule to no-interpolation-in-snapshots * chore: remove changes from CHANGELOG.md * docs: update rule table with new name * fix: apply suggestions from code review Co-authored-by: Gareth Jones <[email protected]> Co-authored-by: Simen Bekkhus <[email protected]> * test: add test to cover missing branch * docs: regenerate table with the new script * fix: edit rule description Co-authored-by: Gareth Jones <[email protected]> Co-authored-by: Simen Bekkhus <[email protected]>
1 parent 00523e9 commit 8d2c17c

6 files changed

+251
-42
lines changed

README.md

+42-41
Original file line numberDiff line numberDiff line change
@@ -128,47 +128,48 @@ installations requiring long-term consistency.
128128

129129
<!-- begin rules list -->
130130

131-
| Rule | Description | Configurations | Fixable |
132-
| ---------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------- | ------------ |
133-
| [consistent-test-it](docs/rules/consistent-test-it.md) | Have control over `test` and `it` usages | | ![fixable][] |
134-
| [expect-expect](docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | ![recommended][] | |
135-
| [lowercase-name](docs/rules/lowercase-name.md) | Enforce lowercase test names | | ![fixable][] |
136-
| [no-alias-methods](docs/rules/no-alias-methods.md) | Disallow alias methods | ![style][] | ![fixable][] |
137-
| [no-commented-out-tests](docs/rules/no-commented-out-tests.md) | Disallow commented out tests | ![recommended][] | |
138-
| [no-conditional-expect](docs/rules/no-conditional-expect.md) | Prevent calling `expect` conditionally | | |
139-
| [no-deprecated-functions](docs/rules/no-deprecated-functions.md) | Disallow use of deprecated functions | | ![fixable][] |
140-
| [no-disabled-tests](docs/rules/no-disabled-tests.md) | Disallow disabled tests | ![recommended][] | |
141-
| [no-duplicate-hooks](docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks | | |
142-
| [no-export](docs/rules/no-export.md) | Disallow using `exports` in files containing tests | ![recommended][] | |
143-
| [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests | ![recommended][] | ![fixable][] |
144-
| [no-hooks](docs/rules/no-hooks.md) | Disallow setup and teardown hooks | | |
145-
| [no-identical-title](docs/rules/no-identical-title.md) | Disallow identical titles | ![recommended][] | |
146-
| [no-if](docs/rules/no-if.md) | Disallow conditional logic | | |
147-
| [no-jasmine-globals](docs/rules/no-jasmine-globals.md) | Disallow Jasmine globals | ![recommended][] | ![fixable][] |
148-
| [no-jest-import](docs/rules/no-jest-import.md) | Disallow importing Jest | ![recommended][] | |
149-
| [no-large-snapshots](docs/rules/no-large-snapshots.md) | disallow large snapshots | | |
150-
| [no-mocks-import](docs/rules/no-mocks-import.md) | Disallow manually importing from `__mocks__` | ![recommended][] | |
151-
| [no-restricted-matchers](docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers | | |
152-
| [no-standalone-expect](docs/rules/no-standalone-expect.md) | Disallow using `expect` outside of `it` or `test` blocks | ![recommended][] | |
153-
| [no-test-callback](docs/rules/no-test-callback.md) | Avoid using a callback in asynchronous tests | ![recommended][] | ![suggest][] |
154-
| [no-test-prefixes](docs/rules/no-test-prefixes.md) | Use `.only` and `.skip` over `f` and `x` | ![recommended][] | ![fixable][] |
155-
| [no-test-return-statement](docs/rules/no-test-return-statement.md) | Disallow explicitly returning from tests | | |
156-
| [prefer-called-with](docs/rules/prefer-called-with.md) | Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()` | | |
157-
| [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | ![suggest][] |
158-
| [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | |
159-
| [prefer-spy-on](docs/rules/prefer-spy-on.md) | Suggest using `jest.spyOn()` | | ![fixable][] |
160-
| [prefer-strict-equal](docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | ![suggest][] |
161-
| [prefer-to-be-null](docs/rules/prefer-to-be-null.md) | Suggest using `toBeNull()` | ![style][] | ![fixable][] |
162-
| [prefer-to-be-undefined](docs/rules/prefer-to-be-undefined.md) | Suggest using `toBeUndefined()` | ![style][] | ![fixable][] |
163-
| [prefer-to-contain](docs/rules/prefer-to-contain.md) | Suggest using `toContain()` | ![style][] | ![fixable][] |
164-
| [prefer-to-have-length](docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | ![style][] | ![fixable][] |
165-
| [prefer-todo](docs/rules/prefer-todo.md) | Suggest using `test.todo` | | ![fixable][] |
166-
| [require-to-throw-message](docs/rules/require-to-throw-message.md) | Require a message for `toThrow()` | | |
167-
| [require-top-level-describe](docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `describe` block | | |
168-
| [valid-describe](docs/rules/valid-describe.md) | Enforce valid `describe()` callback | ![recommended][] | |
169-
| [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage | ![recommended][] | |
170-
| [valid-expect-in-promise](docs/rules/valid-expect-in-promise.md) | Enforce having return statement when testing with promises | ![recommended][] | |
171-
| [valid-title](docs/rules/valid-title.md) | Enforce valid titles | | ![fixable][] |
131+
| Rule | Description | Configurations | Fixable |
132+
| ---------------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------- | ------------ |
133+
| [consistent-test-it](docs/rules/consistent-test-it.md) | Have control over `test` and `it` usages | | ![fixable][] |
134+
| [expect-expect](docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | ![recommended][] | |
135+
| [lowercase-name](docs/rules/lowercase-name.md) | Enforce lowercase test names | | ![fixable][] |
136+
| [no-alias-methods](docs/rules/no-alias-methods.md) | Disallow alias methods | ![style][] | ![fixable][] |
137+
| [no-commented-out-tests](docs/rules/no-commented-out-tests.md) | Disallow commented out tests | ![recommended][] | |
138+
| [no-conditional-expect](docs/rules/no-conditional-expect.md) | Prevent calling `expect` conditionally | | |
139+
| [no-deprecated-functions](docs/rules/no-deprecated-functions.md) | Disallow use of deprecated functions | | ![fixable][] |
140+
| [no-disabled-tests](docs/rules/no-disabled-tests.md) | Disallow disabled tests | ![recommended][] | |
141+
| [no-duplicate-hooks](docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks | | |
142+
| [no-export](docs/rules/no-export.md) | Disallow using `exports` in files containing tests | ![recommended][] | |
143+
| [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests | ![recommended][] | ![fixable][] |
144+
| [no-hooks](docs/rules/no-hooks.md) | Disallow setup and teardown hooks | | |
145+
| [no-identical-title](docs/rules/no-identical-title.md) | Disallow identical titles | ![recommended][] | |
146+
| [no-if](docs/rules/no-if.md) | Disallow conditional logic | | |
147+
| [no-interpolation-in-snapshots](docs/rules/no-interpolation-in-snapshots.md) | Disallow string interpolation inside snapshots | | |
148+
| [no-jasmine-globals](docs/rules/no-jasmine-globals.md) | Disallow Jasmine globals | ![recommended][] | ![fixable][] |
149+
| [no-jest-import](docs/rules/no-jest-import.md) | Disallow importing Jest | ![recommended][] | |
150+
| [no-large-snapshots](docs/rules/no-large-snapshots.md) | disallow large snapshots | | |
151+
| [no-mocks-import](docs/rules/no-mocks-import.md) | Disallow manually importing from `__mocks__` | ![recommended][] | |
152+
| [no-restricted-matchers](docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers | | |
153+
| [no-standalone-expect](docs/rules/no-standalone-expect.md) | Disallow using `expect` outside of `it` or `test` blocks | ![recommended][] | |
154+
| [no-test-callback](docs/rules/no-test-callback.md) | Avoid using a callback in asynchronous tests | ![recommended][] | ![suggest][] |
155+
| [no-test-prefixes](docs/rules/no-test-prefixes.md) | Use `.only` and `.skip` over `f` and `x` | ![recommended][] | ![fixable][] |
156+
| [no-test-return-statement](docs/rules/no-test-return-statement.md) | Disallow explicitly returning from tests | | |
157+
| [prefer-called-with](docs/rules/prefer-called-with.md) | Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()` | | |
158+
| [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | ![suggest][] |
159+
| [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | |
160+
| [prefer-spy-on](docs/rules/prefer-spy-on.md) | Suggest using `jest.spyOn()` | | ![fixable][] |
161+
| [prefer-strict-equal](docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | ![suggest][] |
162+
| [prefer-to-be-null](docs/rules/prefer-to-be-null.md) | Suggest using `toBeNull()` | ![style][] | ![fixable][] |
163+
| [prefer-to-be-undefined](docs/rules/prefer-to-be-undefined.md) | Suggest using `toBeUndefined()` | ![style][] | ![fixable][] |
164+
| [prefer-to-contain](docs/rules/prefer-to-contain.md) | Suggest using `toContain()` | ![style][] | ![fixable][] |
165+
| [prefer-to-have-length](docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | ![style][] | ![fixable][] |
166+
| [prefer-todo](docs/rules/prefer-todo.md) | Suggest using `test.todo` | | ![fixable][] |
167+
| [require-to-throw-message](docs/rules/require-to-throw-message.md) | Require a message for `toThrow()` | | |
168+
| [require-top-level-describe](docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `describe` block | | |
169+
| [valid-describe](docs/rules/valid-describe.md) | Enforce valid `describe()` callback | ![recommended][] | |
170+
| [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage | ![recommended][] | |
171+
| [valid-expect-in-promise](docs/rules/valid-expect-in-promise.md) | Enforce having return statement when testing with promises | ![recommended][] | |
172+
| [valid-title](docs/rules/valid-title.md) | Enforce valid titles | | ![fixable][] |
172173

173174
<!-- end rules list -->
174175

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Disallow string interpolation inside snapshots (`no-interpolation-in-snapshots`)
2+
3+
Prevents the use of string interpolations in snapshots.
4+
5+
## Rule Details
6+
7+
Interpolation prevents snapshots from being updated. Instead, properties should
8+
be overloaded with a matcher by using
9+
[property matchers](https://jestjs.io/docs/en/snapshot-testing#property-matchers).
10+
11+
Examples of **incorrect** code for this rule:
12+
13+
```js
14+
expect(something).toMatchInlineSnapshot(
15+
`Object {
16+
property: ${interpolated}
17+
}`,
18+
);
19+
20+
expect(something).toMatchInlineSnapshot(
21+
{ other: expect.any(Number) },
22+
`Object {
23+
other: Any<Number>,
24+
property: ${interpolated}
25+
}`,
26+
);
27+
28+
expect(errorThrowingFunction).toThrowErrorMatchingInlineSnapshot(
29+
`${interpolated}`,
30+
);
31+
```
32+
33+
Examples of **correct** code for this rule:
34+
35+
```js
36+
expect(something).toMatchInlineSnapshot();
37+
38+
expect(something).toMatchInlineSnapshot(
39+
`Object {
40+
property: 1
41+
}`,
42+
);
43+
44+
expect(something).toMatchInlineSnapshot(
45+
{ property: expect.any(Date) },
46+
`Object {
47+
property: Any<Date>
48+
}`,
49+
);
50+
51+
expect(errorThrowingFunction).toThrowErrorMatchingInlineSnapshot();
52+
53+
expect(errorThrowingFunction).toThrowErrorMatchingInlineSnapshot(
54+
`Error Message`,
55+
);
56+
```
57+
58+
## When Not To Use It
59+
60+
Don't use this rule on non-jest test files.

src/__tests__/__snapshots__/rules.test.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Object {
2525
"jest/no-hooks": "error",
2626
"jest/no-identical-title": "error",
2727
"jest/no-if": "error",
28+
"jest/no-interpolation-in-snapshots": "error",
2829
"jest/no-jasmine-globals": "error",
2930
"jest/no-jest-import": "error",
3031
"jest/no-large-snapshots": "error",

src/__tests__/rules.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { resolve } from 'path';
33
import plugin from '../';
44

55
const ruleNames = Object.keys(plugin.rules);
6-
const numberOfRules = 43;
6+
const numberOfRules = 44;
77

88
describe('rules', () => {
99
it('should have a corresponding doc for each rule', () => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { TSESLint } from '@typescript-eslint/experimental-utils';
2+
import resolveFrom from 'resolve-from';
3+
import rule from '../no-interpolation-in-snapshots';
4+
5+
const ruleTester = new TSESLint.RuleTester({
6+
parser: resolveFrom(require.resolve('eslint'), 'espree'),
7+
parserOptions: {
8+
ecmaVersion: 2017,
9+
},
10+
});
11+
12+
ruleTester.run('no-interpolation-in-snapshots', rule, {
13+
valid: [
14+
'expect("something").toEqual("else");',
15+
'expect(something).toMatchInlineSnapshot();',
16+
'expect(something).toMatchInlineSnapshot(`No interpolation`);',
17+
'expect(something).toMatchInlineSnapshot({}, `No interpolation`);',
18+
'expect(something);',
19+
'expect(something).not;',
20+
'expect.toHaveAssertions();',
21+
'myObjectWants.toMatchInlineSnapshot({}, `${interpolated}`);',
22+
'myObjectWants.toMatchInlineSnapshot({}, `${interpolated1} ${interpolated2}`);',
23+
'toMatchInlineSnapshot({}, `${interpolated}`);',
24+
'toMatchInlineSnapshot({}, `${interpolated1} ${interpolated2}`);',
25+
'expect(something).toThrowErrorMatchingInlineSnapshot();',
26+
'expect(something).toThrowErrorMatchingInlineSnapshot(`No interpolation`);',
27+
],
28+
invalid: [
29+
{
30+
code: 'expect(something).toMatchInlineSnapshot(`${interpolated}`);',
31+
errors: [
32+
{
33+
endColumn: 58,
34+
column: 41,
35+
messageId: 'noInterpolation',
36+
},
37+
],
38+
},
39+
{
40+
code: 'expect(something).not.toMatchInlineSnapshot(`${interpolated}`);',
41+
errors: [
42+
{
43+
endColumn: 62,
44+
column: 45,
45+
messageId: 'noInterpolation',
46+
},
47+
],
48+
},
49+
{
50+
code: 'expect(something).toMatchInlineSnapshot({}, `${interpolated}`);',
51+
errors: [
52+
{
53+
endColumn: 62,
54+
column: 45,
55+
messageId: 'noInterpolation',
56+
},
57+
],
58+
},
59+
{
60+
code:
61+
'expect(something).not.toMatchInlineSnapshot({}, `${interpolated}`);',
62+
errors: [
63+
{
64+
endColumn: 66,
65+
column: 49,
66+
messageId: 'noInterpolation',
67+
},
68+
],
69+
},
70+
{
71+
code:
72+
'expect(something).toThrowErrorMatchingInlineSnapshot(`${interpolated}`);',
73+
errors: [
74+
{
75+
endColumn: 71,
76+
column: 54,
77+
messageId: 'noInterpolation',
78+
},
79+
],
80+
},
81+
{
82+
code:
83+
'expect(something).not.toThrowErrorMatchingInlineSnapshot(`${interpolated}`);',
84+
errors: [
85+
{
86+
endColumn: 75,
87+
column: 58,
88+
messageId: 'noInterpolation',
89+
},
90+
],
91+
},
92+
],
93+
});

0 commit comments

Comments
 (0)