Skip to content

Commit fb4cc67

Browse files
authoredMar 8, 2024··
feat: add support for expectTypeOf to expect-expect (#386)
1 parent 3d36317 commit fb4cc67

File tree

5 files changed

+68
-4
lines changed

5 files changed

+68
-4
lines changed
 

‎README.md

+16
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,22 @@ export default [
108108
];
109109
```
110110

111+
#### Enabling with type-testing
112+
113+
Vitest ships with an optional [type-testing feature](https://vitest.dev/guide/testing-types), which is disabled by default.
114+
115+
If you're using this feature, you should also enabled `typecheck` in the settings for this plugin. This ensures that rules like [expect-expect](docs/rules/expect-expect.md) account for type-related assertions in tests.
116+
117+
```json
118+
{
119+
"extends": ["plugin:vitest/recommended"],
120+
"settings" :{
121+
"vitest": {
122+
"typecheck": true,
123+
}
124+
}
125+
}
126+
```
111127

112128
### Rules
113129

‎docs/rules/expect-expect.md

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ test('myLogic', () => {
2828
})
2929
```
3030

31+
## Type-testing
32+
33+
If you're using Vitest's [type-testing feature](https://vitest.dev/guide/testing-types) and have tests that only contain `expectTypeOf`, you will need to enable `typecheck` in this plugin's settings: [Enabling type-testing](../../README.md#enabling-with-type-testing).
34+
3135
## Options
3236

3337
### `assertFunctionNames`

‎src/rules/expect-expect.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'
22
import { createEslintRule, getNodeName, isSupportedAccessor } from '../utils'
3+
import { parsePluginSettings } from '../utils/parsePluginSettings'
34
import { getTestCallExpressionsFromDeclaredVariables, isTypeOfVitestFnCall } from '../utils/parseVitestFnCall'
45

56
export const RULE_NAME = 'expect-expect'
@@ -62,6 +63,9 @@ export default createEslintRule<Options, MESSAGE_ID>({
6263
defaultOptions: [{ assertFunctionNames: ['expect'], additionalTestBlockFunctions: [] }],
6364
create(context, [{ assertFunctionNames = ['expect'], additionalTestBlockFunctions = [] }]) {
6465
const unchecked: TSESTree.CallExpression[] = []
66+
const settings = parsePluginSettings(context.settings)
67+
68+
if (settings.typecheck) assertFunctionNames.push('expectTypeOf')
6569

6670
function checkCallExpression(nodes: TSESTree.Node[]) {
6771
for (const node of nodes) {
@@ -82,12 +86,11 @@ export default createEslintRule<Options, MESSAGE_ID>({
8286
}
8387

8488
return {
85-
CallExpression(node) {
86-
if (node?.callee?.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === 'skip')
89+
CallExpression(node) {
90+
if (node?.callee?.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === 'skip')
8791
return
88-
89-
const name = getNodeName(node) ?? ''
9092

93+
const name = getNodeName(node) ?? ''
9194

9295
if (isTypeOfVitestFnCall(node, context, ['test']) || additionalTestBlockFunctions.includes(name)) {
9396
if (node.callee.type === AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.callee.property, 'todo')) return

‎src/utils/parsePluginSettings.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { SharedConfigurationSettings } from '@typescript-eslint/utils/dist/ts-eslint'
2+
3+
interface PluginSettings {
4+
typecheck: boolean;
5+
}
6+
7+
const DEFAULTS: PluginSettings = {
8+
typecheck: false
9+
}
10+
11+
export function parsePluginSettings(settings: SharedConfigurationSettings): PluginSettings {
12+
const pluginSettings = typeof settings.vitest !== 'object' || settings.vitest === null
13+
? {}
14+
: settings.vitest
15+
16+
return {
17+
...DEFAULTS,
18+
...pluginSettings
19+
}
20+
}

‎tests/expect-expect.test.ts

+21
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ ruleTester.run(RULE_NAME, rule, {
155155
`,
156156
options: [{ assertFunctionNames: ['tester.foo.bar.expect'] }],
157157
parserOptions: { sourceType: 'module' }
158+
},
159+
{
160+
code: `
161+
it("should pass with 'typecheck' enabled", () => {
162+
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: number }>()
163+
});
164+
`,
165+
settings: { vitest: { typecheck: true } }
158166
}
159167
],
160168
invalid: [
@@ -304,6 +312,19 @@ ruleTester.run(RULE_NAME, rule, {
304312
type: AST_NODE_TYPES.Identifier
305313
}
306314
]
315+
},
316+
{
317+
code: `
318+
it("should fail without 'typecheck' enabled", () => {
319+
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: number }>()
320+
});
321+
`,
322+
errors: [
323+
{
324+
messageId: 'noAssertions',
325+
type: AST_NODE_TYPES.Identifier
326+
}
327+
]
307328
}
308329
]
309330
})

0 commit comments

Comments
 (0)
Please sign in to comment.