js-style-kit 0.6.1 → 0.8.0
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 +5 -0
- package/dist/bin/index.cjs +2 -1
- package/dist/bin/index.cjs.map +1 -1
- package/dist/index.d.ts +23 -7
- package/dist/index.js +135 -36
- package/dist/index.js.map +1 -1
- package/package.json +10 -7
- package/src/eslint/base/README.md +186 -0
- package/src/eslint/base/config.ts +37 -0
- package/src/eslint/base/rules.ts +444 -0
- package/src/eslint/base/types.ts +20 -0
- package/src/eslint/constants.ts +52 -0
- package/src/eslint/convex/README.md +30 -0
- package/src/eslint/convex/config.ts +34 -0
- package/src/eslint/convex/rules.ts +8 -0
- package/src/eslint/convex/types.ts +8 -0
- package/src/eslint/ignores.ts +34 -0
- package/src/eslint/import/README.md +397 -0
- package/src/eslint/import/config.ts +48 -0
- package/src/eslint/import/rules.ts +81 -0
- package/src/eslint/index.ts +273 -0
- package/src/eslint/jsdoc/README.md +399 -0
- package/src/eslint/jsdoc/config.ts +29 -0
- package/src/eslint/jsdoc/rules.ts +81 -0
- package/src/eslint/jsdoc/types.ts +56 -0
- package/src/eslint/nextjs/config.ts +25 -0
- package/src/eslint/nextjs/rules.ts +25 -0
- package/src/eslint/nextjs/types.ts +27 -0
- package/src/eslint/perfectionist/README.md +454 -0
- package/src/eslint/perfectionist/config.ts +25 -0
- package/src/eslint/perfectionist/rules.ts +39 -0
- package/src/eslint/prefer-arrow-function/config.ts +33 -0
- package/src/eslint/prefer-arrow-function/types.ts +13 -0
- package/src/eslint/process-custom-rules.ts +72 -0
- package/src/eslint/query/README.md +254 -0
- package/src/eslint/query/config.ts +27 -0
- package/src/eslint/query/rules.ts +11 -0
- package/src/eslint/query/types.ts +11 -0
- package/src/eslint/react/README.md +416 -0
- package/src/eslint/react/config.ts +65 -0
- package/src/eslint/react/rules.ts +188 -0
- package/src/eslint/react/types.ts +26 -0
- package/src/eslint/react-refresh/config.ts +28 -0
- package/src/eslint/react-refresh/rules.ts +48 -0
- package/src/eslint/storybook/README.md +424 -0
- package/src/eslint/storybook/config.ts +57 -0
- package/src/eslint/testing/README.md +436 -0
- package/src/eslint/testing/config.ts +99 -0
- package/src/eslint/testing/get-import-restrictions.ts +70 -0
- package/src/eslint/testing/jest-rules.ts +47 -0
- package/src/eslint/testing/vitest-rules.ts +42 -0
- package/src/eslint/turbo/README.md +380 -0
- package/src/eslint/turbo/config.ts +26 -0
- package/src/eslint/turbo/types.ts +7 -0
- package/src/eslint/types.ts +36 -0
- package/src/eslint/typescript/README.md +229 -0
- package/src/eslint/typescript/config.ts +48 -0
- package/src/eslint/typescript/rules.ts +137 -0
- package/src/eslint/typescript/types.ts +35 -0
- package/src/eslint/unicorn/README.md +497 -0
- package/src/eslint/unicorn/config.ts +36 -0
- package/src/eslint/unicorn/rules.ts +86 -0
- package/src/index.ts +3 -0
- package/src/modules.d.ts +5 -0
- package/src/prettier/README.md +413 -0
- package/src/prettier/index.ts +110 -0
- package/src/utils/is-type.ts +60 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# Testing Configuration
|
|
2
|
+
|
|
3
|
+
Comprehensive testing support with rules for Vitest, Jest, Bun, and Node.js test runners.
|
|
4
|
+
|
|
5
|
+
[← Back to main README](../../../README.md)
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Testing support is **disabled by default** and provides:
|
|
10
|
+
|
|
11
|
+
- Framework-specific test rules (Vitest, Jest, Bun, Node.js)
|
|
12
|
+
- Test file naming conventions
|
|
13
|
+
- Test style consistency (`it` vs `test`)
|
|
14
|
+
- Optional formatting rules for test block padding
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import { eslintConfig } from "js-style-kit";
|
|
20
|
+
|
|
21
|
+
export default eslintConfig({
|
|
22
|
+
testing: true, // Enable with defaults (Vitest)
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Configuration Options
|
|
27
|
+
|
|
28
|
+
### Basic Enable
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
testing: true, // Vitest + ".test" files + "test" style + formatting
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Framework-Specific
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
testing: {
|
|
38
|
+
framework: "vitest", // "vitest" | "jest" | "bun" | "node"
|
|
39
|
+
filenamePattern: "test", // "test" | "spec"
|
|
40
|
+
itOrTest: "test", // "test" | "it"
|
|
41
|
+
formattingRules: true, // Enable padding rules
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Custom Files
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
testing: {
|
|
49
|
+
framework: "jest",
|
|
50
|
+
files: ["**/__tests__/**/*.ts", "**/*.test.ts"],
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Framework Options
|
|
55
|
+
|
|
56
|
+
### Vitest (Default)
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
export default eslintConfig({
|
|
60
|
+
testing: { framework: "vitest" },
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Includes:**
|
|
65
|
+
|
|
66
|
+
- ✅ Vitest-specific rules and globals
|
|
67
|
+
- ✅ Test assertion best practices
|
|
68
|
+
- ✅ Hook usage validation
|
|
69
|
+
|
|
70
|
+
### Jest
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
export default eslintConfig({
|
|
74
|
+
testing: { framework: "jest" },
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Includes:**
|
|
79
|
+
|
|
80
|
+
- ✅ Jest-specific rules and globals
|
|
81
|
+
- ✅ All Jest best practices
|
|
82
|
+
- ✅ Snapshot testing support
|
|
83
|
+
|
|
84
|
+
### Bun
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
export default eslintConfig({
|
|
88
|
+
testing: { framework: "bun" },
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Includes:**
|
|
93
|
+
|
|
94
|
+
- ✅ Bun test runner support
|
|
95
|
+
- ✅ Uses `bun:test` global package
|
|
96
|
+
- ✅ Jest-compatible rules
|
|
97
|
+
|
|
98
|
+
### Node.js
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
export default eslintConfig({
|
|
102
|
+
testing: { framework: "node" },
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Includes:**
|
|
107
|
+
|
|
108
|
+
- ✅ Node.js native test runner
|
|
109
|
+
- ✅ Uses `node:test` global package
|
|
110
|
+
- ✅ Jest-compatible rules
|
|
111
|
+
|
|
112
|
+
## Filename Patterns
|
|
113
|
+
|
|
114
|
+
Control test file naming conventions:
|
|
115
|
+
|
|
116
|
+
### .test files (Default)
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
testing: {
|
|
120
|
+
filenamePattern: "test", // Enforces .test.ts, .test.tsx, etc.
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Valid:**
|
|
125
|
+
|
|
126
|
+
- ✅ `user.test.ts`
|
|
127
|
+
- ✅ `components/button.test.tsx`
|
|
128
|
+
- ❌ `user.spec.ts` (warning)
|
|
129
|
+
|
|
130
|
+
### .spec files
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
testing: {
|
|
134
|
+
filenamePattern: "spec", // Enforces .spec.ts, .spec.tsx, etc.
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Valid:**
|
|
139
|
+
|
|
140
|
+
- ✅ `user.spec.ts`
|
|
141
|
+
- ✅ `components/button.spec.tsx`
|
|
142
|
+
- ❌ `user.test.ts` (warning)
|
|
143
|
+
|
|
144
|
+
## Test Style
|
|
145
|
+
|
|
146
|
+
Choose between `it` and `test` function names:
|
|
147
|
+
|
|
148
|
+
### "test" Style (Default)
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
testing: {
|
|
152
|
+
itOrTest: "test",
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
// ✅ Good
|
|
158
|
+
test("adds two numbers", () => {
|
|
159
|
+
expect(add(1, 2)).toBe(3);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// ❌ Bad
|
|
163
|
+
it("adds two numbers", () => {
|
|
164
|
+
expect(add(1, 2)).toBe(3);
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### "it" Style
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
testing: {
|
|
172
|
+
itOrTest: "it",
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
// ✅ Good
|
|
178
|
+
it("should add two numbers", () => {
|
|
179
|
+
expect(add(1, 2)).toBe(3);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ❌ Bad
|
|
183
|
+
test("should add two numbers", () => {
|
|
184
|
+
expect(add(1, 2)).toBe(3);
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Formatting Rules
|
|
189
|
+
|
|
190
|
+
Control padding around test blocks for better readability:
|
|
191
|
+
|
|
192
|
+
### Enabled (Default)
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
testing: {
|
|
196
|
+
formattingRules: true,
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
// ✅ Good - proper spacing
|
|
202
|
+
describe("Calculator", () => {
|
|
203
|
+
let calculator;
|
|
204
|
+
|
|
205
|
+
beforeEach(() => {
|
|
206
|
+
calculator = new Calculator();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
afterEach(() => {
|
|
210
|
+
calculator.reset();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("addition", () => {
|
|
214
|
+
test("adds positive numbers", () => {
|
|
215
|
+
expect(calculator.add(2, 3)).toBe(5);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("adds negative numbers", () => {
|
|
219
|
+
expect(calculator.add(-2, -3)).toBe(-5);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// ❌ Bad - no spacing
|
|
225
|
+
describe("Calculator", () => {
|
|
226
|
+
let calculator;
|
|
227
|
+
beforeEach(() => {
|
|
228
|
+
calculator = new Calculator();
|
|
229
|
+
});
|
|
230
|
+
afterEach(() => {
|
|
231
|
+
calculator.reset();
|
|
232
|
+
});
|
|
233
|
+
describe("addition", () => {
|
|
234
|
+
test("adds positive numbers", () => {
|
|
235
|
+
expect(calculator.add(2, 3)).toBe(5);
|
|
236
|
+
});
|
|
237
|
+
test("adds negative numbers", () => {
|
|
238
|
+
expect(calculator.add(-2, -3)).toBe(-5);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Disabled
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
testing: {
|
|
248
|
+
formattingRules: false, // No padding requirements
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Key Rules
|
|
253
|
+
|
|
254
|
+
### Test Structure
|
|
255
|
+
|
|
256
|
+
- **`vitest/consistent-test-filename`** - Enforces consistent file naming
|
|
257
|
+
- **`vitest/consistent-test-it`** / **`jest/consistent-test-it`** - Enforces `it` or `test`
|
|
258
|
+
- **`vitest/no-identical-title`** / **`jest/no-identical-title`** - Prevents duplicate test names
|
|
259
|
+
|
|
260
|
+
### Assertions
|
|
261
|
+
|
|
262
|
+
- **`vitest/expect-expect`** / **`jest/expect-expect`** - Ensures tests have assertions
|
|
263
|
+
- **`vitest/no-conditional-expect`** / **`jest/no-conditional-expect`** - No conditional assertions
|
|
264
|
+
- **`vitest/valid-expect`** / **`jest/valid-expect`** - Validates expect usage
|
|
265
|
+
|
|
266
|
+
### Best Practices
|
|
267
|
+
|
|
268
|
+
- **`vitest/prefer-to-be`** / **`jest/prefer-to-be`** - Use `toBe()` for primitives
|
|
269
|
+
- **`vitest/prefer-to-be-truthy`** / **`jest/prefer-to-be-truthy`** - Use `toBeTruthy()`/`toBeFalsy()`
|
|
270
|
+
- **`vitest/no-disabled-tests`** / **`jest/no-disabled-tests`** - Warns on `.skip` and `.only`
|
|
271
|
+
|
|
272
|
+
### TypeScript Integration
|
|
273
|
+
|
|
274
|
+
When TypeScript is enabled:
|
|
275
|
+
|
|
276
|
+
- **`@typescript-eslint/unbound-method`** is disabled (conflicts with test mocking)
|
|
277
|
+
|
|
278
|
+
## Examples
|
|
279
|
+
|
|
280
|
+
### Vitest + TypeScript
|
|
281
|
+
|
|
282
|
+
```js
|
|
283
|
+
export default eslintConfig({
|
|
284
|
+
typescript: true,
|
|
285
|
+
testing: {
|
|
286
|
+
framework: "vitest",
|
|
287
|
+
filenamePattern: "test",
|
|
288
|
+
itOrTest: "test",
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
// user.test.ts
|
|
295
|
+
import { describe, test, expect } from "vitest";
|
|
296
|
+
import { createUser } from "./user";
|
|
297
|
+
|
|
298
|
+
describe("createUser", () => {
|
|
299
|
+
test("creates a user with valid data", () => {
|
|
300
|
+
const user = createUser({ name: "Alice", age: 30 });
|
|
301
|
+
expect(user).toEqual({ name: "Alice", age: 30 });
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("throws on invalid age", () => {
|
|
305
|
+
expect(() => createUser({ name: "Bob", age: -1 })).toThrow();
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Jest + Spec Files
|
|
311
|
+
|
|
312
|
+
```js
|
|
313
|
+
export default eslintConfig({
|
|
314
|
+
testing: {
|
|
315
|
+
framework: "jest",
|
|
316
|
+
filenamePattern: "spec",
|
|
317
|
+
itOrTest: "it",
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
// user.spec.ts
|
|
324
|
+
describe("User", () => {
|
|
325
|
+
it("should create a user", () => {
|
|
326
|
+
const user = new User("Alice");
|
|
327
|
+
expect(user.name).toBe("Alice");
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Bun Tests
|
|
333
|
+
|
|
334
|
+
```js
|
|
335
|
+
export default eslintConfig({
|
|
336
|
+
testing: {
|
|
337
|
+
framework: "bun",
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
// math.test.ts
|
|
344
|
+
import { test, expect } from "bun:test";
|
|
345
|
+
|
|
346
|
+
test("adds numbers", () => {
|
|
347
|
+
expect(1 + 2).toBe(3);
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Node.js Tests
|
|
352
|
+
|
|
353
|
+
```js
|
|
354
|
+
export default eslintConfig({
|
|
355
|
+
testing: {
|
|
356
|
+
framework: "node",
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
// math.test.ts
|
|
363
|
+
import { test } from "node:test";
|
|
364
|
+
import assert from "node:assert";
|
|
365
|
+
|
|
366
|
+
test("adds numbers", () => {
|
|
367
|
+
assert.strictEqual(1 + 2, 3);
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Customization
|
|
372
|
+
|
|
373
|
+
Override specific testing rules:
|
|
374
|
+
|
|
375
|
+
```js
|
|
376
|
+
export default eslintConfig({
|
|
377
|
+
testing: { framework: "vitest" },
|
|
378
|
+
rules: {
|
|
379
|
+
// Allow .only for debugging
|
|
380
|
+
"vitest/no-disabled-tests": "off",
|
|
381
|
+
|
|
382
|
+
// Stricter test naming
|
|
383
|
+
"vitest/consistent-test-filename": "error",
|
|
384
|
+
|
|
385
|
+
// Disable padding rules
|
|
386
|
+
"jest/padding-around-test-blocks": "off",
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Common Patterns
|
|
392
|
+
|
|
393
|
+
### Monorepo with Multiple Test Runners
|
|
394
|
+
|
|
395
|
+
```js
|
|
396
|
+
export default eslintConfig({
|
|
397
|
+
testing: { framework: "vitest" },
|
|
398
|
+
overrides: [
|
|
399
|
+
{
|
|
400
|
+
files: ["packages/legacy/**/*.test.ts"],
|
|
401
|
+
rules: {
|
|
402
|
+
// Use Jest rules for legacy package
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
});
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### React Component Testing
|
|
410
|
+
|
|
411
|
+
```js
|
|
412
|
+
export default eslintConfig({
|
|
413
|
+
typescript: true,
|
|
414
|
+
react: true,
|
|
415
|
+
testing: {
|
|
416
|
+
framework: "vitest",
|
|
417
|
+
files: ["**/*.test.{ts,tsx}"],
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Related Configurations
|
|
423
|
+
|
|
424
|
+
- [TypeScript](../typescript/README.md) - TypeScript configuration
|
|
425
|
+
- [React](../react/README.md) - React component testing
|
|
426
|
+
- [Base](../base/README.md) - Base ESLint rules
|
|
427
|
+
|
|
428
|
+
## Learn More
|
|
429
|
+
|
|
430
|
+
- [Vitest](https://vitest.dev/)
|
|
431
|
+
- [Jest](https://jestjs.io/)
|
|
432
|
+
- [Bun Test](https://bun.sh/docs/cli/test)
|
|
433
|
+
- [Node.js Test Runner](https://nodejs.org/api/test.html)
|
|
434
|
+
- [eslint-plugin-vitest](https://github.com/vitest-dev/eslint-plugin-vitest)
|
|
435
|
+
- [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest)
|
|
436
|
+
- [Main README](../../../README.md)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import jest from "eslint-plugin-jest";
|
|
2
|
+
import vitest from "eslint-plugin-vitest";
|
|
3
|
+
|
|
4
|
+
import type { EslintConfigObject, EslintRuleConfig } from "../types.js";
|
|
5
|
+
|
|
6
|
+
import { configNames } from "../constants.js";
|
|
7
|
+
import { getImportRestrictions } from "./get-import-restrictions.js";
|
|
8
|
+
import { jestRules } from "./jest-rules.js";
|
|
9
|
+
import { vitestRules } from "./vitest-rules.js";
|
|
10
|
+
|
|
11
|
+
export interface TestingConfig {
|
|
12
|
+
filenamePattern?: "spec" | "test";
|
|
13
|
+
files?: string[];
|
|
14
|
+
formattingRules?: boolean;
|
|
15
|
+
framework?: "bun" | "jest" | "node" | "vitest";
|
|
16
|
+
/**
|
|
17
|
+
* Whether to enforce imports from the correct testing framework.
|
|
18
|
+
* Uses the built-in ESLint `no-restricted-imports` rule.
|
|
19
|
+
*
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
importRestrictions?: boolean;
|
|
23
|
+
itOrTest?: "it" | "test";
|
|
24
|
+
typescript?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates an ESLint configuration object for testing.
|
|
29
|
+
*
|
|
30
|
+
* @param options - Configuration options
|
|
31
|
+
* @param options.files - Files to include in the configuration
|
|
32
|
+
* @param options.filenamePattern - ".test" or ".spec" filename pattern
|
|
33
|
+
* @param options.itOrTest - "it" or "test"
|
|
34
|
+
* @param options.framework - "jest" or "vitest" or "bun" or "node"
|
|
35
|
+
* @param options.formattingRules - Whether to include formatting rules like padding around blocks
|
|
36
|
+
* @param options.importRestrictions - Whether to enforce imports from the correct testing framework
|
|
37
|
+
* @param options.typescript - Whether the user is using TypeScript
|
|
38
|
+
* @param customRules - Optional object containing custom rules to override or add to the testing configuration.
|
|
39
|
+
* @returns ESLint configuration object
|
|
40
|
+
*/
|
|
41
|
+
export const testingConfig = (
|
|
42
|
+
{
|
|
43
|
+
filenamePattern = "test",
|
|
44
|
+
files,
|
|
45
|
+
formattingRules = true,
|
|
46
|
+
framework = "vitest",
|
|
47
|
+
importRestrictions = true,
|
|
48
|
+
itOrTest = "test",
|
|
49
|
+
typescript = true,
|
|
50
|
+
}: TestingConfig = {},
|
|
51
|
+
customRules?: Record<string, EslintRuleConfig>,
|
|
52
|
+
): EslintConfigObject => ({
|
|
53
|
+
files: files ?? ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
|
|
54
|
+
languageOptions: {
|
|
55
|
+
globals:
|
|
56
|
+
framework === "vitest" ?
|
|
57
|
+
{ ...vitest.environments.env.globals }
|
|
58
|
+
: jest.environments.globals.globals,
|
|
59
|
+
},
|
|
60
|
+
name: configNames.testing,
|
|
61
|
+
plugins: {
|
|
62
|
+
jest,
|
|
63
|
+
vitest,
|
|
64
|
+
},
|
|
65
|
+
rules: {
|
|
66
|
+
...(typescript ? { "@typescript-eslint/unbound-method": "off" } : {}),
|
|
67
|
+
// jest doesn't have a file name rule, so we'll use this one for both
|
|
68
|
+
"vitest/consistent-test-filename": [
|
|
69
|
+
"warn",
|
|
70
|
+
{
|
|
71
|
+
allTestPattern: ".*\\.(test|spec)\\.[tj]sx?$",
|
|
72
|
+
pattern: `.*\\.${filenamePattern}\\.[tj]sx?$`,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
...(framework === "vitest" ? vitestRules(itOrTest) : jestRules(itOrTest)),
|
|
76
|
+
...(formattingRules ?
|
|
77
|
+
{
|
|
78
|
+
"jest/padding-around-after-all-blocks": "warn",
|
|
79
|
+
"jest/padding-around-after-each-blocks": "warn",
|
|
80
|
+
"jest/padding-around-before-all-blocks": "warn",
|
|
81
|
+
"jest/padding-around-before-each-blocks": "warn",
|
|
82
|
+
"jest/padding-around-describe-blocks": "warn",
|
|
83
|
+
"jest/padding-around-expect-groups": "warn",
|
|
84
|
+
"jest/padding-around-test-blocks": "warn",
|
|
85
|
+
}
|
|
86
|
+
: {}),
|
|
87
|
+
...(importRestrictions ? getImportRestrictions(framework) : {}),
|
|
88
|
+
...(customRules ?? {}),
|
|
89
|
+
},
|
|
90
|
+
...(framework !== "jest" && framework !== "vitest" ?
|
|
91
|
+
{
|
|
92
|
+
settings: {
|
|
93
|
+
jest: {
|
|
94
|
+
globalPackage: framework === "node" ? "node:test" : "bun:test",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
: {}),
|
|
99
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { EslintRuleConfig } from "../types.js";
|
|
2
|
+
|
|
3
|
+
const commonTestImports = [
|
|
4
|
+
"describe",
|
|
5
|
+
"it",
|
|
6
|
+
"test",
|
|
7
|
+
"expect",
|
|
8
|
+
"beforeAll",
|
|
9
|
+
"beforeEach",
|
|
10
|
+
"afterAll",
|
|
11
|
+
"afterEach",
|
|
12
|
+
"vi",
|
|
13
|
+
"mock",
|
|
14
|
+
"spyOn",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const frameworkConfig = {
|
|
18
|
+
bun: {
|
|
19
|
+
allowed: "'bun:test'",
|
|
20
|
+
restricted: ["vitest", "jest", "@jest/globals", "node:test"],
|
|
21
|
+
},
|
|
22
|
+
jest: {
|
|
23
|
+
allowed: "'jest' or '@jest/globals'",
|
|
24
|
+
restricted: ["vitest", "bun:test", "node:test"],
|
|
25
|
+
},
|
|
26
|
+
node: {
|
|
27
|
+
allowed: "'node:test'",
|
|
28
|
+
restricted: ["vitest", "jest", "@jest/globals", "bun:test"],
|
|
29
|
+
},
|
|
30
|
+
vitest: {
|
|
31
|
+
allowed: "'vitest'",
|
|
32
|
+
restricted: ["jest", "@jest/globals", "bun:test", "node:test"],
|
|
33
|
+
},
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generates the error message for restricted imports.
|
|
38
|
+
*
|
|
39
|
+
* @param allowedFramework - The allowed framework(s) for imports
|
|
40
|
+
* @returns The formatted error message
|
|
41
|
+
*/
|
|
42
|
+
const getRestrictionMessage = (allowedFramework: string): string =>
|
|
43
|
+
`This project is setup to use ${allowedFramework} for testing. Importing from other testing frameworks is not allowed. Change this setting in eslint.config.js under testing.framework`;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Returns import restriction rules based on the testing framework.
|
|
47
|
+
* Prevents importing from the wrong testing framework.
|
|
48
|
+
*
|
|
49
|
+
* @param framework - The testing framework being used
|
|
50
|
+
* @returns ESLint rules object with import restrictions
|
|
51
|
+
*/
|
|
52
|
+
export const getImportRestrictions = (
|
|
53
|
+
framework: "bun" | "jest" | "node" | "vitest",
|
|
54
|
+
): Record<string, EslintRuleConfig> => {
|
|
55
|
+
const config = frameworkConfig[framework];
|
|
56
|
+
const message = getRestrictionMessage(config.allowed);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
"no-restricted-imports": [
|
|
60
|
+
"warn",
|
|
61
|
+
{
|
|
62
|
+
paths: config.restricted.map((name) => ({
|
|
63
|
+
importNames: commonTestImports,
|
|
64
|
+
message,
|
|
65
|
+
name,
|
|
66
|
+
})),
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { EslintRuleConfig } from "../types.js";
|
|
2
|
+
|
|
3
|
+
type JestRules = Record<`jest/${string}`, EslintRuleConfig>;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates an object containing the ESLint rules for jest.
|
|
7
|
+
*
|
|
8
|
+
* @param itOrTest - "it" or "test"
|
|
9
|
+
* @returns An object containing the ESLint rules for jest.
|
|
10
|
+
*/
|
|
11
|
+
export const jestRules = (itOrTest: "it" | "test" = "test"): JestRules => ({
|
|
12
|
+
"jest/consistent-test-it": [
|
|
13
|
+
"warn",
|
|
14
|
+
{ fn: itOrTest, withinDescribe: itOrTest },
|
|
15
|
+
],
|
|
16
|
+
"jest/expect-expect": "warn",
|
|
17
|
+
"jest/no-commented-out-tests": "warn",
|
|
18
|
+
"jest/no-conditional-expect": "warn",
|
|
19
|
+
"jest/no-conditional-in-test": "warn",
|
|
20
|
+
"jest/no-disabled-tests": "warn",
|
|
21
|
+
"jest/no-duplicate-hooks": "warn",
|
|
22
|
+
"jest/no-focused-tests": "warn",
|
|
23
|
+
"jest/no-identical-title": "warn",
|
|
24
|
+
"jest/no-interpolation-in-snapshots": "warn",
|
|
25
|
+
"jest/no-large-snapshots": ["warn", { inlineMaxSize: 50, maxSize: 100 }],
|
|
26
|
+
"jest/no-mocks-import": "warn", // Discourage manually importing from __mocks__
|
|
27
|
+
"jest/no-standalone-expect": "warn",
|
|
28
|
+
"jest/no-test-prefixes": "warn", // Prefer .only and .skip over f and x
|
|
29
|
+
"jest/no-test-return-statement": "warn",
|
|
30
|
+
"jest/prefer-comparison-matcher": "warn",
|
|
31
|
+
"jest/prefer-equality-matcher": "warn",
|
|
32
|
+
"jest/prefer-expect-resolves": "warn",
|
|
33
|
+
"jest/prefer-hooks-in-order": "warn",
|
|
34
|
+
"jest/prefer-hooks-on-top": "warn",
|
|
35
|
+
"jest/prefer-lowercase-title": ["warn", { ignoreTopLevelDescribe: true }],
|
|
36
|
+
"jest/prefer-snapshot-hint": "warn",
|
|
37
|
+
"jest/prefer-spy-on": "warn",
|
|
38
|
+
"jest/prefer-strict-equal": "warn",
|
|
39
|
+
"jest/prefer-to-be": "warn",
|
|
40
|
+
"jest/prefer-to-contain": "warn",
|
|
41
|
+
"jest/prefer-to-have-length": "warn",
|
|
42
|
+
"jest/require-top-level-describe": "warn",
|
|
43
|
+
"jest/valid-describe-callback": "warn",
|
|
44
|
+
"jest/valid-expect": "warn",
|
|
45
|
+
"jest/valid-expect-in-promise": "warn",
|
|
46
|
+
"jest/valid-title": "warn",
|
|
47
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { EslintRuleConfig } from "../types.js";
|
|
2
|
+
|
|
3
|
+
type VitestRules = Record<`vitest/${string}`, EslintRuleConfig>;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates an object containing the ESLint rules for vitest.
|
|
7
|
+
*
|
|
8
|
+
* @param itOrTest - "it" or "test"
|
|
9
|
+
* @returns An object containing the ESLint rules for vitest.
|
|
10
|
+
*/
|
|
11
|
+
export const vitestRules = (itOrTest: "it" | "test" = "test"): VitestRules => ({
|
|
12
|
+
"vitest/consistent-test-it": [
|
|
13
|
+
"warn",
|
|
14
|
+
{ fn: itOrTest, withinDescribe: itOrTest },
|
|
15
|
+
],
|
|
16
|
+
"vitest/expect-expect": "warn",
|
|
17
|
+
"vitest/no-commented-out-tests": "warn",
|
|
18
|
+
"vitest/no-conditional-in-test": "warn",
|
|
19
|
+
"vitest/no-disabled-tests": "warn",
|
|
20
|
+
"vitest/no-duplicate-hooks": "warn",
|
|
21
|
+
"vitest/no-focused-tests": "warn",
|
|
22
|
+
"vitest/no-identical-title": "warn",
|
|
23
|
+
"vitest/no-import-node-test": "warn",
|
|
24
|
+
"vitest/no-interpolation-in-snapshots": "warn", // Avoid dynamic snapshots
|
|
25
|
+
"vitest/no-large-snapshots": ["warn", { inlineMaxSize: 50, maxSize: 100 }], // Keep snapshots manageable
|
|
26
|
+
"vitest/no-standalone-expect": "warn",
|
|
27
|
+
"vitest/no-test-return-statement": "warn", // Tests shouldn't return values
|
|
28
|
+
"vitest/prefer-comparison-matcher": "warn", // Use comparison matchers
|
|
29
|
+
"vitest/prefer-equality-matcher": "warn", // Use equality matchers
|
|
30
|
+
"vitest/prefer-hooks-in-order": "warn", // Keep hooks in a predictable order
|
|
31
|
+
"vitest/prefer-hooks-on-top": "warn", // Keep hooks organized
|
|
32
|
+
"vitest/prefer-lowercase-title": ["warn", { ignoreTopLevelDescribe: true }], // Consistent casing
|
|
33
|
+
"vitest/prefer-strict-equal": "warn", // Prefer .toStrictEqual() over .toEqual()
|
|
34
|
+
"vitest/prefer-to-be": "warn", // Use .toBe() for primitives
|
|
35
|
+
"vitest/prefer-to-contain": "warn", // Use .toContain() for array/string includes
|
|
36
|
+
"vitest/prefer-to-have-length": "warn", // Use .toHaveLength() for checking length
|
|
37
|
+
"vitest/require-local-test-context-for-concurrent-snapshots": "warn",
|
|
38
|
+
"vitest/require-top-level-describe": "warn", // Group tests in describe blocks
|
|
39
|
+
"vitest/valid-describe-callback": "warn",
|
|
40
|
+
"vitest/valid-expect": "warn",
|
|
41
|
+
"vitest/valid-title": "warn",
|
|
42
|
+
});
|