|
| 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