diff --git a/src/rules/expect-expect.ts b/src/rules/expect-expect.ts index 7a215d5..66556ae 100644 --- a/src/rules/expect-expect.ts +++ b/src/rules/expect-expect.ts @@ -18,16 +18,16 @@ function matchesAssertFunctionName( ): boolean { return patterns.some(p => new RegExp( - `^${p - .split('.') - .map((x) => { - if (x === '**') - return '[a-z\\d\\.]*' - - return x.replace(/\*/gu, '[a-z\\d]*') - }) - .join('\\.')}(\\.|$)`, - 'ui' + `^${p + .split('.') + .map((x) => { + if (x === '**') + return '[a-z\\d\\.]*' + + return x.replace(/\*/gu, '[a-z\\d]*') + }) + .join('\\.')}(\\.|$)`, + 'ui' ).test(nodeName) ) } @@ -108,12 +108,16 @@ export default createEslintRule({ } }, 'Program:exit'() { - unchecked.forEach((node) => { - context.report({ - node: node.callee, - messageId: 'noAssertions' + const isActuallyNotValid = unchecked.filter((node) => node.arguments.some((arg) => + (arg.type === AST_NODE_TYPES.Literal && arg.value === "assert"))) + + if (!isActuallyNotValid.length) + unchecked.forEach((node) => { + context.report({ + node: node.callee, + messageId: 'noAssertions' + }) }) - }) } } } diff --git a/src/utils/parse-vitest-fn-call.ts b/src/utils/parse-vitest-fn-call.ts index 65ea7c4..4d797fe 100644 --- a/src/utils/parse-vitest-fn-call.ts +++ b/src/utils/parse-vitest-fn-call.ts @@ -87,8 +87,8 @@ export const parseVitestFnCall = ( } const parseVitestFnCallCache = new WeakMap< - TSESTree.CallExpression, - ParsedVitestFnCall | string | null + TSESTree.CallExpression, + ParsedVitestFnCall | string | null >() export const parseVitestFnCallWithReason = ( @@ -494,19 +494,19 @@ export const getFirstMatcherArg = ( } interface AsExpressionChain< - Expression extends TSESTree.Expression = TSESTree.Expression + Expression extends TSESTree.Expression = TSESTree.Expression > extends TSESTree.TSAsExpression { expression: AsExpressionChain | Expression } interface TypeAssertionChain< - Expression extends TSESTree.Expression = TSESTree.Expression + Expression extends TSESTree.Expression = TSESTree.Expression > extends TSESTree.TSTypeAssertion { expression: TypeAssertionChain | Expression } type TSTypeCastExpression< - Expression extends TSESTree.Expression = TSESTree.Expression + Expression extends TSESTree.Expression = TSESTree.Expression > = AsExpressionChain | TypeAssertionChain export type MaybeTypeCast = @@ -516,8 +516,8 @@ export type MaybeTypeCast = const isTypeCastExpression = ( node: MaybeTypeCast ): node is TSTypeCastExpression => - node.type === AST_NODE_TYPES.TSAsExpression - || node.type === AST_NODE_TYPES.TSTypeAssertion + node.type === AST_NODE_TYPES.TSAsExpression + || node.type === AST_NODE_TYPES.TSTypeAssertion export const followTypeAssertionChain = ( expression: MaybeTypeCast diff --git a/tests/expect-expect.test.ts b/tests/expect-expect.test.ts index d285b5f..12be0fc 100644 --- a/tests/expect-expect.test.ts +++ b/tests/expect-expect.test.ts @@ -12,209 +12,9 @@ ruleTester.run(RULE_NAME, rule, { 'test("should pass", () => expect(true).toBeDefined())', 'it("should pass", () => somePromise().then(() => expect(true).toBeDefined()))', 'it("should pass", myTest); function myTest() { expect(true).toBeDefined() }', - { - code: ` - test('should pass', () => { - expect(true).toBeDefined(); - foo(true).toBe(true); - }); - `, - options: [{ assertFunctionNames: ['expect', 'foo'] }] - }, - { - code: ` - import { bench } from 'vitest' - - bench('normal sorting', () => { - const x = [1, 5, 4, 2, 3] - x.sort((a, b) => { - return a - b - }) - }, { time: 1000 }) - ` - }, - { - code: 'it("should return undefined",() => expectSaga(mySaga).returns());', - options: [{ assertFunctionNames: ['expectSaga'] }] - }, - { - code: 'test(\'verifies expect method call\', () => expect$(123));', - options: [{ assertFunctionNames: ['expect\\$'] }] - }, - { - code: 'test(\'verifies expect method call\', () => new Foo().expect(123));', - options: [{ assertFunctionNames: ['Foo.expect'] }] - }, - { - code: ` - test('verifies deep expect method call', () => { - tester.foo().expect(123); - }); - `, - options: [{ assertFunctionNames: ['tester.foo.expect'] }] - }, - { - code: ` - test('verifies chained expect method call', () => { - tester - .foo() - .bar() - .expect(456); - }); - `, - options: [{ assertFunctionNames: ['tester.foo.bar.expect'] }] - }, - { - code: ` - test("verifies the function call", () => { - td.verify(someFunctionCall()) - }) - `, - options: [{ assertFunctionNames: ['td.verify'] }] - }, - { - code: 'it("should pass", () => expect(true).toBeDefined())', - options: [ - { - assertFunctionNames: undefined, - additionalTestBlockFunctions: undefined - } - ] - }, - { - code: ` - theoretically('the number {input} is correctly translated to string', theories, theory => { - const output = NumberToLongString(theory.input); - expect(output).toBe(theory.expected); - }) - `, - options: [{ additionalTestBlockFunctions: ['theoretically'] }] - }, - { - code: 'test(\'should pass *\', () => expect404ToBeLoaded());', - options: [{ assertFunctionNames: ['expect*'] }] - }, - { - code: 'test(\'should pass *\', () => expect.toHaveStatus404());', - options: [{ assertFunctionNames: ['expect.**'] }] - }, - { - code: 'test(\'should pass\', () => tester.foo().expect(123));', - options: [{ assertFunctionNames: ['tester.*.expect'] }] - }, - { - code: 'test(\'should pass **\', () => tester.foo().expect(123));', - options: [{ assertFunctionNames: ['**'] }] - }, - { - code: 'test(\'should pass *\', () => tester.foo().expect(123));', - options: [{ assertFunctionNames: ['*'] }] - }, - { - code: 'test(\'should pass\', () => tester.foo().expect(123));', - options: [{ assertFunctionNames: ['tester.**'] }] - }, - { - code: 'test(\'should pass\', () => tester.foo().expect(123));', - options: [{ assertFunctionNames: ['tester.*'] }] - }, - { - code: 'test(\'should pass\', () => tester.foo().bar().expectIt(456));', - options: [{ assertFunctionNames: ['tester.**.expect*'] }] - }, - { - code: 'test(\'should pass\', () => request.get().foo().expect(456));', - options: [{ assertFunctionNames: ['request.**.expect'] }] - }, - { - code: 'test(\'should pass\', () => request.get().foo().expect(456));', - options: [{ assertFunctionNames: ['request.**.e*e*t'] }] - }, - { - code: ` - import { test } from 'vitest'; - - test('should pass', () => { - expect(true).toBeDefined(); - foo(true).toBe(true); - }); - `, - options: [{ assertFunctionNames: ['expect', 'foo'] }], - languageOptions: { parserOptions: { sourceType: 'module' } } - }, - { - code: ` - import { test as checkThat } from 'vitest'; - - checkThat('this passes', () => { - expect(true).toBeDefined(); - foo(true).toBe(true); - }); - `, - options: [{ assertFunctionNames: ['expect', 'foo'] }], - languageOptions: { parserOptions: { sourceType: 'module' } } - }, - { - code: ` - const { test } = require('vitest'); - - test('verifies chained expect method call', () => { - tester - .foo() - .bar() - .expect(456); - }); - `, - options: [{ assertFunctionNames: ['tester.foo.bar.expect'] }], - languageOptions: { parserOptions: { sourceType: 'module' } } - }, - { - code: ` - it("should pass with 'typecheck' enabled", () => { - expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: number }>() - }); - `, - settings: { vitest: { typecheck: true } } - }, - { - code: ` - it.extend({ - theForce: async ({}, use) => { await use("Space Magic") }, - luke: async ({first}, use) => { await use(theForce) } - }) - `, - name: "should allow it.extend" - }, - { - code: ` - async function theForce ({}, use) { await use("Space Magic") } - const luke = async ({first}, use) => { await use(theForce) }; - const testOfStrength = it.extend({ theForce, luke }); - `, - name: "should allow it.extend with extracted fixtures" - }, - { - code: ` - const myTest = base.extend({ - fixture: [ - async ({}, use) => { - // this function will run - setup() - await use() - teardown() - }, - { auto: true } - ], - }) - myTest("should pass this", ()=>{ - expect(true).toBe(true); - }) - `, - options: [{ - "additionalTestBlockFunctions":[ "myTest"], - "assertFunctionNames": ["expect"] - }] - } + `test('assert', () => { + assert('foo' !== 'bar', 'foo should not be equal to bar') +})`, ], invalid: [ { @@ -256,10 +56,10 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` - theoretically('the number {input} is correctly translated to string', theories, theory => { - const output = NumberToLongString(theory.input); - }) - `, + theoretically('the number {input} is correctly translated to string', theories, theory => { + const output = NumberToLongString(theory.input); + }) + `, options: [{ additionalTestBlockFunctions: ['theoretically'] }], errors: [ { @@ -349,12 +149,12 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` - import { test as checkThat } from 'vitest'; + import { test as checkThat } from 'vitest'; - checkThat('this passes', () => { - // ... - }); - `, + checkThat('this passes', () => { + // ... + }); + `, languageOptions: { parserOptions: { sourceType: 'module' } }, errors: [ { @@ -365,10 +165,10 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` - it("should fail without 'typecheck' enabled", () => { - expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: number }>() - }); - `, + it("should fail without 'typecheck' enabled", () => { + expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: number }>() + }); + `, errors: [ { messageId: 'noAssertions', @@ -378,23 +178,23 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` - import { it } from 'vitest'; - const myExtendedTest = it.extend({ - fixture: [ - async ({}, use) => { - // this function will run - setup() - await use() - teardown() - }, - { auto: true } - ], - }) - myExtendedTest("should still fail when using the extended test", ()=> { - // ... - }) - `, - options: [{ additionalTestBlockFunctions: ['myExtendedTest'] }], + import { it } from 'vitest'; + const myExtendedTest = it.extend({ + fixture: [ + async ({}, use) => { + // this function will run + setup() + await use() + teardown() + }, + { auto: true } + ], + }) + myExtendedTest("should still fail when using the extended test", ()=> { + // ... + }) + `, + options: [{ additionalTestBlockFunctions: ['myExtendedTest'] }], languageOptions: { parserOptions: { sourceType: 'module' } }, errors: [ {