Skip to content

Commit

Permalink
fix(expect-expect): make sure rules doesn't break when assert is used (
Browse files Browse the repository at this point in the history
  • Loading branch information
veritem committed Aug 14, 2024
1 parent 2fb0d3f commit 65cc22c
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 255 deletions.
34 changes: 19 additions & 15 deletions src/rules/expect-expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
}
Expand Down Expand Up @@ -108,12 +108,16 @@ export default createEslintRule<Options, MESSAGE_ID>({
}
},
'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'
})
})
})
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/utils/parse-vitest-fn-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ export const parseVitestFnCall = (
}

const parseVitestFnCallCache = new WeakMap<
TSESTree.CallExpression,
ParsedVitestFnCall | string | null
TSESTree.CallExpression,
ParsedVitestFnCall | string | null
>()

export const parseVitestFnCallWithReason = (
Expand Down Expand Up @@ -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> | Expression
}

interface TypeAssertionChain<
Expression extends TSESTree.Expression = TSESTree.Expression
Expression extends TSESTree.Expression = TSESTree.Expression
> extends TSESTree.TSTypeAssertion {
expression: TypeAssertionChain<Expression> | Expression
}

type TSTypeCastExpression<
Expression extends TSESTree.Expression = TSESTree.Expression
Expression extends TSESTree.Expression = TSESTree.Expression
> = AsExpressionChain<Expression> | TypeAssertionChain<Expression>

export type MaybeTypeCast<Expression extends TSESTree.Expression> =
Expand All @@ -516,8 +516,8 @@ export type MaybeTypeCast<Expression extends TSESTree.Expression> =
const isTypeCastExpression = <Expression extends TSESTree.Expression>(
node: MaybeTypeCast<Expression>
): node is TSTypeCastExpression<Expression> =>
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 extends TSESTree.Expression>(
expression: MaybeTypeCast<Expression>
Expand Down
266 changes: 33 additions & 233 deletions tests/expect-expect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand All @@ -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',
Expand All @@ -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: [
{
Expand Down

0 comments on commit 65cc22c

Please sign in to comment.