eslint-plugin-jest 26.1.0-next.2 → 26.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -183,6 +183,7 @@ installations requiring long-term consistency.
183
183
  | [prefer-expect-resolves](docs/rules/prefer-expect-resolves.md) | Prefer `await expect(...).resolves` over `expect(await ...)` syntax | | ![fixable][] |
184
184
  | [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | |
185
185
  | [prefer-lowercase-title](docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | ![fixable][] |
186
+ | [prefer-snapshot-hint](docs/rules/prefer-snapshot-hint.md) | Prefer including a hint with external snapshots | | |
186
187
  | [prefer-spy-on](docs/rules/prefer-spy-on.md) | Suggest using `jest.spyOn()` | | ![fixable][] |
187
188
  | [prefer-strict-equal](docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | ![suggest][] |
188
189
  | [prefer-to-be](docs/rules/prefer-to-be.md) | Suggest using `toBe()` for primitive literals | ![style][] | ![fixable][] |
@@ -118,7 +118,7 @@ a native loop.
118
118
  "rules": {
119
119
  "jest/prefer-expect-assertions": [
120
120
  "warn",
121
- { "onlyFunctionsWithAsyncKeyword": true }
121
+ { "onlyFunctionsWithExpectInLoop": true }
122
122
  ]
123
123
  }
124
124
  }
@@ -0,0 +1,188 @@
1
+ # Prefer including a hint with external snapshots (`prefer-snapshot-hint`)
2
+
3
+ When working with external snapshot matchers it's considered best practice to
4
+ provide a hint (as the last argument to the matcher) describing the expected
5
+ snapshot content that will be included in the snapshots name by Jest.
6
+
7
+ This makes it easier for reviewers to verify the snapshots during review, and
8
+ for anyone to know whether an outdated snapshot is the correct behavior before
9
+ updating.
10
+
11
+ ## Rule details
12
+
13
+ This rule looks for any use of an external snapshot matcher (e.g.
14
+ `toMatchSnapshot` and `toThrowErrorMatchingSnapshot`) and checks if they include
15
+ a snapshot hint.
16
+
17
+ ## Options
18
+
19
+ ### `'always'`
20
+
21
+ Require a hint to _always_ be provided when using external snapshot matchers.
22
+
23
+ Examples of **incorrect** code for the `'always'` option:
24
+
25
+ ```js
26
+ const snapshotOutput = ({ stdout, stderr }) => {
27
+ expect(stdout).toMatchSnapshot();
28
+ expect(stderr).toMatchSnapshot();
29
+ };
30
+
31
+ describe('cli', () => {
32
+ describe('--version flag', () => {
33
+ it('prints the version', async () => {
34
+ snapshotOutput(await runCli(['--version']));
35
+ });
36
+ });
37
+
38
+ describe('--config flag', () => {
39
+ it('reads the config', async () => {
40
+ const { stdout, parsedConfig } = await runCli([
41
+ '--config',
42
+ 'jest.config.js',
43
+ ]);
44
+
45
+ expect(stdout).toMatchSnapshot();
46
+ expect(parsedConfig).toMatchSnapshot();
47
+ });
48
+
49
+ it('prints nothing to stderr', async () => {
50
+ const { stderr } = await runCli(['--config', 'jest.config.js']);
51
+
52
+ expect(stderr).toMatchSnapshot();
53
+ });
54
+
55
+ describe('when the file does not exist', () => {
56
+ it('throws an error', async () => {
57
+ await expect(
58
+ runCli(['--config', 'does-not-exist.js']),
59
+ ).rejects.toThrowErrorMatchingSnapshot();
60
+ });
61
+ });
62
+ });
63
+ });
64
+ ```
65
+
66
+ Examples of **correct** code for the `'always'` option:
67
+
68
+ ```js
69
+ const snapshotOutput = ({ stdout, stderr }, hints) => {
70
+ expect(stdout).toMatchSnapshot({}, `stdout: ${hints.stdout}`);
71
+ expect(stderr).toMatchSnapshot({}, `stderr: ${hints.stderr}`);
72
+ };
73
+
74
+ describe('cli', () => {
75
+ describe('--version flag', () => {
76
+ it('prints the version', async () => {
77
+ snapshotOutput(await runCli(['--version']), {
78
+ stdout: 'version string',
79
+ stderr: 'empty',
80
+ });
81
+ });
82
+ });
83
+
84
+ describe('--config flag', () => {
85
+ it('reads the config', async () => {
86
+ const { stdout } = await runCli(['--config', 'jest.config.js']);
87
+
88
+ expect(stdout).toMatchSnapshot({}, 'stdout: config settings');
89
+ });
90
+
91
+ it('prints nothing to stderr', async () => {
92
+ const { stderr } = await runCli(['--config', 'jest.config.js']);
93
+
94
+ expect(stderr).toMatchInlineSnapshot();
95
+ });
96
+
97
+ describe('when the file does not exist', () => {
98
+ it('throws an error', async () => {
99
+ await expect(
100
+ runCli(['--config', 'does-not-exist.js']),
101
+ ).rejects.toThrowErrorMatchingSnapshot('stderr: config error');
102
+ });
103
+ });
104
+ });
105
+ });
106
+ ```
107
+
108
+ ### `'multi'` (default)
109
+
110
+ Require a hint to be provided when there are multiple external snapshot matchers
111
+ within the scope (meaning it includes nested calls).
112
+
113
+ Examples of **incorrect** code for the `'multi'` option:
114
+
115
+ ```js
116
+ const snapshotOutput = ({ stdout, stderr }) => {
117
+ expect(stdout).toMatchSnapshot();
118
+ expect(stderr).toMatchSnapshot();
119
+ };
120
+
121
+ describe('cli', () => {
122
+ describe('--version flag', () => {
123
+ it('prints the version', async () => {
124
+ snapshotOutput(await runCli(['--version']));
125
+ });
126
+ });
127
+
128
+ describe('--config flag', () => {
129
+ it('reads the config', async () => {
130
+ const { stdout, parsedConfig } = await runCli([
131
+ '--config',
132
+ 'jest.config.js',
133
+ ]);
134
+
135
+ expect(stdout).toMatchSnapshot();
136
+ expect(parsedConfig).toMatchSnapshot();
137
+ });
138
+
139
+ it('prints nothing to stderr', async () => {
140
+ const { stderr } = await runCli(['--config', 'jest.config.js']);
141
+
142
+ expect(stderr).toMatchSnapshot();
143
+ });
144
+ });
145
+ });
146
+ ```
147
+
148
+ Examples of **correct** code for the `'multi'` option:
149
+
150
+ ```js
151
+ const snapshotOutput = ({ stdout, stderr }, hints) => {
152
+ expect(stdout).toMatchSnapshot({}, `stdout: ${hints.stdout}`);
153
+ expect(stderr).toMatchSnapshot({}, `stderr: ${hints.stderr}`);
154
+ };
155
+
156
+ describe('cli', () => {
157
+ describe('--version flag', () => {
158
+ it('prints the version', async () => {
159
+ snapshotOutput(await runCli(['--version']), {
160
+ stdout: 'version string',
161
+ stderr: 'empty',
162
+ });
163
+ });
164
+ });
165
+
166
+ describe('--config flag', () => {
167
+ it('reads the config', async () => {
168
+ const { stdout } = await runCli(['--config', 'jest.config.js']);
169
+
170
+ expect(stdout).toMatchSnapshot();
171
+ });
172
+
173
+ it('prints nothing to stderr', async () => {
174
+ const { stderr } = await runCli(['--config', 'jest.config.js']);
175
+
176
+ expect(stderr).toMatchInlineSnapshot();
177
+ });
178
+
179
+ describe('when the file does not exist', () => {
180
+ it('throws an error', async () => {
181
+ await expect(
182
+ runCli(['--config', 'does-not-exist.js']),
183
+ ).rejects.toThrowErrorMatchingSnapshot();
184
+ });
185
+ });
186
+ });
187
+ });
188
+ ```
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _utils = require("./utils");
9
+
10
+ const snapshotMatchers = ['toMatchSnapshot', 'toThrowErrorMatchingSnapshot'];
11
+
12
+ const isSnapshotMatcher = matcher => {
13
+ return snapshotMatchers.includes(matcher.name);
14
+ };
15
+
16
+ const isSnapshotMatcherWithoutHint = matcher => {
17
+ if (!matcher.arguments || matcher.arguments.length === 0) {
18
+ return true;
19
+ } // this matcher only supports one argument which is the hint
20
+
21
+
22
+ if (matcher.name !== 'toMatchSnapshot') {
23
+ return matcher.arguments.length !== 1;
24
+ } // if we're being passed two arguments,
25
+ // the second one should be the hint
26
+
27
+
28
+ if (matcher.arguments.length === 2) {
29
+ return false;
30
+ }
31
+
32
+ const [arg] = matcher.arguments; // the first argument to `toMatchSnapshot` can be _either_ a snapshot hint or
33
+ // an object with asymmetric matchers, so we can't just assume that the first
34
+ // argument is a hint when it's by itself.
35
+
36
+ return !(0, _utils.isStringNode)(arg);
37
+ };
38
+
39
+ const messages = {
40
+ missingHint: 'You should provide a hint for this snapshot'
41
+ };
42
+
43
+ var _default = (0, _utils.createRule)({
44
+ name: __filename,
45
+ meta: {
46
+ docs: {
47
+ category: 'Best Practices',
48
+ description: 'Prefer including a hint with external snapshots',
49
+ recommended: false
50
+ },
51
+ messages,
52
+ type: 'suggestion',
53
+ schema: [{
54
+ type: 'string',
55
+ enum: ['always', 'multi']
56
+ }]
57
+ },
58
+ defaultOptions: ['multi'],
59
+
60
+ create(context, [mode]) {
61
+ const snapshotMatchers = [];
62
+ let expressionDepth = 0;
63
+
64
+ const reportSnapshotMatchersWithoutHints = () => {
65
+ for (const snapshotMatcher of snapshotMatchers) {
66
+ if (isSnapshotMatcherWithoutHint(snapshotMatcher)) {
67
+ context.report({
68
+ messageId: 'missingHint',
69
+ node: snapshotMatcher.node.property
70
+ });
71
+ }
72
+ }
73
+ };
74
+
75
+ const enterExpression = () => {
76
+ expressionDepth++;
77
+ };
78
+
79
+ const exitExpression = () => {
80
+ expressionDepth--;
81
+
82
+ if (mode === 'always') {
83
+ reportSnapshotMatchersWithoutHints();
84
+ snapshotMatchers.length = 0;
85
+ }
86
+
87
+ if (mode === 'multi' && expressionDepth === 0) {
88
+ if (snapshotMatchers.length > 1) {
89
+ reportSnapshotMatchersWithoutHints();
90
+ }
91
+
92
+ snapshotMatchers.length = 0;
93
+ }
94
+ };
95
+
96
+ return {
97
+ 'Program:exit'() {
98
+ enterExpression();
99
+ exitExpression();
100
+ },
101
+
102
+ FunctionExpression: enterExpression,
103
+ 'FunctionExpression:exit': exitExpression,
104
+ ArrowFunctionExpression: enterExpression,
105
+ 'ArrowFunctionExpression:exit': exitExpression,
106
+
107
+ CallExpression(node) {
108
+ if (!(0, _utils.isExpectCall)(node)) {
109
+ return;
110
+ }
111
+
112
+ const {
113
+ matcher
114
+ } = (0, _utils.parseExpectCall)(node);
115
+
116
+ if (!matcher || !isSnapshotMatcher(matcher)) {
117
+ return;
118
+ }
119
+
120
+ snapshotMatchers.push(matcher);
121
+ }
122
+
123
+ };
124
+ }
125
+
126
+ });
127
+
128
+ exports.default = _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-jest",
3
- "version": "26.1.0-next.2",
3
+ "version": "26.1.2",
4
4
  "description": "ESLint rules for Jest",
5
5
  "keywords": [
6
6
  "eslint",
@@ -118,7 +118,7 @@
118
118
  "jest": "^27.0.0",
119
119
  "jest-runner-eslint": "^1.0.0",
120
120
  "lint-staged": "^12.0.0",
121
- "pinst": "^2.0.0",
121
+ "pinst": "^3.0.0",
122
122
  "prettier": "^2.0.5",
123
123
  "rimraf": "^3.0.0",
124
124
  "semantic-release": "^19.0.0",
@@ -163,5 +163,5 @@
163
163
  "@typescript-eslint/experimental-utils": "^5.0.0",
164
164
  "fsevents/node-gyp": "^7.0.0"
165
165
  },
166
- "packageManager": "yarn@3.1.1"
166
+ "packageManager": "yarn@3.2.0"
167
167
  }