Skip to content

Commit

Permalink
feat: add support for expectTypeOf to expect-expect (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmckeb committed Mar 8, 2024
1 parent 3d36317 commit fb4cc67
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 4 deletions.
16 changes: 16 additions & 0 deletions README.md
Expand Up @@ -108,6 +108,22 @@ export default [
];
```

#### Enabling with type-testing

Vitest ships with an optional [type-testing feature](https://vitest.dev/guide/testing-types), which is disabled by default.

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.

```json
{
"extends": ["plugin:vitest/recommended"],
"settings" :{
"vitest": {
"typecheck": true,
}
}
}
```

### Rules

Expand Down
4 changes: 4 additions & 0 deletions docs/rules/expect-expect.md
Expand Up @@ -28,6 +28,10 @@ test('myLogic', () => {
})
```

## Type-testing

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

## Options

### `assertFunctionNames`
Expand Down
11 changes: 7 additions & 4 deletions src/rules/expect-expect.ts
@@ -1,5 +1,6 @@
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'
import { createEslintRule, getNodeName, isSupportedAccessor } from '../utils'
import { parsePluginSettings } from '../utils/parsePluginSettings'
import { getTestCallExpressionsFromDeclaredVariables, isTypeOfVitestFnCall } from '../utils/parseVitestFnCall'

export const RULE_NAME = 'expect-expect'
Expand Down Expand Up @@ -62,6 +63,9 @@ export default createEslintRule<Options, MESSAGE_ID>({
defaultOptions: [{ assertFunctionNames: ['expect'], additionalTestBlockFunctions: [] }],
create(context, [{ assertFunctionNames = ['expect'], additionalTestBlockFunctions = [] }]) {
const unchecked: TSESTree.CallExpression[] = []
const settings = parsePluginSettings(context.settings)

if (settings.typecheck) assertFunctionNames.push('expectTypeOf')

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

return {
CallExpression(node) {
if (node?.callee?.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === 'skip')
CallExpression(node) {
if (node?.callee?.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === 'skip')
return

const name = getNodeName(node) ?? ''

const name = getNodeName(node) ?? ''

if (isTypeOfVitestFnCall(node, context, ['test']) || additionalTestBlockFunctions.includes(name)) {
if (node.callee.type === AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.callee.property, 'todo')) return
Expand Down
20 changes: 20 additions & 0 deletions src/utils/parsePluginSettings.ts
@@ -0,0 +1,20 @@
import type { SharedConfigurationSettings } from '@typescript-eslint/utils/dist/ts-eslint'

interface PluginSettings {
typecheck: boolean;
}

const DEFAULTS: PluginSettings = {
typecheck: false
}

export function parsePluginSettings(settings: SharedConfigurationSettings): PluginSettings {
const pluginSettings = typeof settings.vitest !== 'object' || settings.vitest === null
? {}
: settings.vitest

return {
...DEFAULTS,
...pluginSettings
}
}
21 changes: 21 additions & 0 deletions tests/expect-expect.test.ts
Expand Up @@ -155,6 +155,14 @@ ruleTester.run(RULE_NAME, rule, {
`,
options: [{ assertFunctionNames: ['tester.foo.bar.expect'] }],
parserOptions: { sourceType: 'module' }
},
{
code: `
it("should pass with 'typecheck' enabled", () => {
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: number }>()
});
`,
settings: { vitest: { typecheck: true } }
}
],
invalid: [
Expand Down Expand Up @@ -304,6 +312,19 @@ ruleTester.run(RULE_NAME, rule, {
type: AST_NODE_TYPES.Identifier
}
]
},
{
code: `
it("should fail without 'typecheck' enabled", () => {
expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: number }>()
});
`,
errors: [
{
messageId: 'noAssertions',
type: AST_NODE_TYPES.Identifier
}
]
}
]
})

0 comments on commit fb4cc67

Please sign in to comment.