Skip to content

Commit ae84459

Browse files
fiskersindresorhus
andauthored
Add docs about tests (#2243)
Co-authored-by: Sindre Sorhus <[email protected]>
1 parent ce44929 commit ae84459

File tree

3 files changed

+187
-5
lines changed

3 files changed

+187
-5
lines changed

.github/contributing.md

+4
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ First open an issue with your proposal. When the rule is accepted, see the [docs
1111
## I want to implement a rule from an open issue
1212

1313
See the [docs on creating and submitting a new rule](../docs/new-rule.md).
14+
15+
## How to write tests
16+
17+
See the [docs on writing tests](../docs/write-tests.md).

docs/new-rule.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ Use the [`astexplorer` site](https://astexplorer.net) with the `espree` parser a
1212

1313
## Steps
1414

15-
- Run `$ npm run create-rule` to create files for the new rule.
16-
- Open “test/{RULE_ID}.mjs” and write some tests before implementing the rule.
15+
- Run `npm run create-rule` to create files for the new rule.
16+
- Open “test/{RULE_ID}.mjs” and [write some tests](./write-tests.md) before implementing the rule.
1717
- Open “rules/{RULE_ID}.js” and implement the rule logic.
1818
- Add the correct [`meta.type`](https://eslint.org/docs/developer-guide/working-with-rules#rule-basics) to the rule.
1919
- Open “docs/rules/{RULE_ID}.js” and write some documentation.
2020
- Double check `configs/recommended.js` and `readme.md`, make sure the new rule is correctly added.
21-
- Run `$ npm test` to ensure the tests pass.
22-
- Run `$ npm run integration` to run the rules against real projects to ensure your rule does not fail on real-world code.
21+
- Run `npm test` to ensure the tests pass.
22+
- Run `npm run integration` to run the rules against real projects to ensure your rule does not fail on real-world code.
2323
- Open a pull request with a title in exactly the format `` Add `rule-name` rule ``, for example, `` Add `no-unused-properties` rule ``.
2424
- The pull request description should include the issue it fixes, for example, `Fixes #123`.
25-
- Run `$ npm run run-rules-on-codebase` to run the rules against codebase to ensure code in the repository are following your rule, _you can ignore this step until your PR is reviewed_.
25+
- Run `npm run run-rules-on-codebase` to run the rules against codebase to ensure code in the repository are following your rule, _you can ignore this step until your PR is reviewed_.

docs/write-tests.md

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Writing tests
2+
3+
Tests are in the `/test` directory.
4+
5+
A rule test file should look like this:
6+
7+
```js
8+
import {getTester} from './utils/test.mjs';
9+
10+
const {test} = getTester(import.meta);
11+
12+
test.snapshot({
13+
valid: [
14+
// Valid test cases goes here
15+
],
16+
invalid: [
17+
// Valid test cases goes here
18+
],
19+
});
20+
```
21+
22+
## `test.snapshot()`
23+
24+
This runs [`SnapshotRuleTester`](../test/utils/snapshot-rule-tester.mjs), which auto-generates the snapshot for test results, including error messages, error locations, autofix result, and suggestions. All you have to do is check the snapshot and make sure the results are expected before committing.
25+
26+
It's recommended to use this approach as it simplifies test writing.
27+
28+
```js
29+
import {getTester} from './utils/test.mjs';
30+
31+
const {test} = getTester(import.meta);
32+
33+
test.snapshot({
34+
valid: [
35+
'valid.code',
36+
],
37+
invalid: [
38+
'invalid.code',
39+
],
40+
});
41+
```
42+
43+
## Focus on one rule
44+
45+
We use [`AVA`](https://github.com/avajs/ava) to run tests. To focus on a specific rule test, you can:
46+
47+
```console
48+
npx ava test/rule-name.mjs
49+
```
50+
51+
To update snapshots, run the command above with [`--update-snapshots` or `-u`](https://github.com/avajs/ava/blob/main/docs/05-command-line.md#cli).
52+
53+
```console
54+
npx ava test/rule-name.mjs -u
55+
```
56+
57+
## Focus on one test case
58+
59+
To focus on a single test case, you can:
60+
61+
```js
62+
test.snapshot({
63+
valid: [],
64+
invalid: [
65+
// Tagged template with `test.only`
66+
test.only`code`,
67+
68+
// Wrap code with `test.only`
69+
test.only('code'),
70+
71+
// Wrap test case with `test.only`
72+
test.only({
73+
code: 'code',
74+
options: [{checkFoo: true}],
75+
}),
76+
77+
// Use `only: true`
78+
{
79+
code: 'code',
80+
options: [{checkFoo: true}],
81+
only: true,
82+
},
83+
],
84+
})
85+
```
86+
87+
**Please remove `test.only` and `only: true` before committing.**
88+
89+
## `test()`
90+
91+
This runs [`eslint-ava-rule-tester`](https://github.com/jfmengels/eslint-ava-rule-tester):
92+
93+
```js
94+
import {getTester} from './utils/test.mjs';
95+
96+
const {test} = getTester(import.meta);
97+
98+
test({
99+
valid: [
100+
'valid.code',
101+
],
102+
invalid: [
103+
{
104+
code: 'invalid.code',
105+
errors: [{ message: 'invalid.code is not allowed', column: 1, line: 1 }],
106+
output: 'fixed.code',
107+
}
108+
],
109+
});
110+
```
111+
112+
## `test.babel()`
113+
114+
Same as `test()`, but uses [`@babel/eslint-parser`](https://www.npmjs.com/package/@babel/eslint-parser) as parser.
115+
116+
## `test.typescript()`
117+
118+
Same as `test()`, but uses [`@typescript-eslint/parser`](https://www.npmjs.com/package/@typescript-eslint/parser) as parser.
119+
120+
## `test.vue()`
121+
122+
Same as `test()`, but uses [`vue-eslint-parser`](https://www.npmjs.com/package/vue-eslint-parser) as parser.
123+
124+
## `testerOptions`
125+
126+
`test` and `test.*()` accepts `testerOptions`, which lets you specify common `parseOptions` to all test cases.
127+
128+
```js
129+
test.snapshot({
130+
testerOptions: {
131+
parserOptions: {
132+
ecmaFeatures: {
133+
jsx: true,
134+
},
135+
},
136+
},
137+
valid: [],
138+
invalid: [],
139+
})
140+
```
141+
142+
## `parsers`
143+
144+
[`utils/test.mjs`](../test/utils/test.mjs) also exposes a `parsers` object, which can be used in `testerOptions` or `parser` for a single test case.
145+
146+
```js
147+
import {getTester, parsers} from './utils/test.mjs';
148+
149+
const {test} = getTester(import.meta);
150+
151+
test.snapshot({
152+
testerOptions: {
153+
parser: parsers.babel,
154+
},
155+
valid: [],
156+
invalid: [],
157+
})
158+
```
159+
160+
```js
161+
import {getTester, parsers} from './utils/test.mjs';
162+
163+
const {test} = getTester(import.meta);
164+
165+
test.snapshot({
166+
valid: [],
167+
invalid: [
168+
{
169+
code: 'invalid.code.parse.by.babel',
170+
parser: parsers.babel,
171+
},
172+
],
173+
})
174+
```
175+
176+
Why use `parser: parsers.babel` instead of `parser: '@babel/eslint-parser'`?
177+
178+
Using `parsers.babel` will make the `parserOptions` merge with useful default options. See [`parser.mjs`](../test/utils/parsers.mjs) for details.

0 commit comments

Comments
 (0)