Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: expect.hasAssertions() / expect.assertions() - Does not work when using expect() bound to current test #595

Merged
merged 3 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/rules/prefer-expect-assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,6 @@ export default createEslintRule<Options[], MessageIds>({

const [, secondArg] = node.arguments

if (secondArg?.type === AST_NODE_TYPES.ArrowFunctionExpression && secondArg.params.length) {
if (secondArg?.params[0].type === AST_NODE_TYPES.ObjectPattern) {
if (secondArg.params[0].properties[0].type === AST_NODE_TYPES.Property && secondArg.params[0].properties[0].key.type === AST_NODE_TYPES.Identifier && secondArg.params[0].properties[0].key.name === "expect") return
}
}

if (!isFunction(secondArg) || !shouldCheckFunction(secondArg))
return

Expand Down
11 changes: 8 additions & 3 deletions src/rules/require-local-test-context-for-concurrent-snapshots.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
import { createEslintRule, isSupportedAccessor } from '../utils'
import { isTypeOfVitestFnCall } from '../utils/parse-vitest-fn-call'
import { isTypeOfVitestFnCall, parseVitestFnCall } from '../utils/parse-vitest-fn-call'

export const RULE_NAME = 'require-local-test-context-for-concurrent-snapshots'

Expand All @@ -21,8 +21,13 @@ export default createEslintRule({
create(context) {
return {
CallExpression(node) {
const isNotAnAssertion = !isTypeOfVitestFnCall(node, context, ['expect'])
if (isNotAnAssertion) return
const vitestFnCall = parseVitestFnCall(node, context)
if (vitestFnCall === null)
return
if (vitestFnCall.type !== "expect")
return
if (vitestFnCall.type === "expect" && vitestFnCall.head.type === "testContext")
return

const isNotASnapshotAssertion = ![
'toMatchSnapshot',
Expand Down
31 changes: 20 additions & 11 deletions src/utils/parse-vitest-fn-call.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AST_NODE_TYPES, TSESLint, TSESTree } from '@typescript-eslint/utils'
import { DescribeAlias, HookName, ModifierName, TestCaseName } from './types'
import { ValidVitestFnCallChains } from './valid-vitest-fn-call-chains'
import { AccessorNode, getAccessorValue, getStringValue, isIdentifier, isStringNode, isSupportedAccessor } from '.'
import { AccessorNode, getAccessorValue, getStringValue, isFunction, isIdentifier, isStringNode, isSupportedAccessor } from '.'

export type VitestFnType =
| 'test'
Expand All @@ -17,7 +17,7 @@ export type VitestFnType =
interface ResolvedVitestFn {
original: string | null
local: string
type: 'import' | 'global'
type: 'import' | 'global' | 'testContext'
}

interface ImportDetails {
Expand Down Expand Up @@ -309,17 +309,11 @@ export function getNodeChain(node: TSESTree.Node): AccessorNode[] | null {
return null
}

interface ResolvedVitestFnType {
original: string | null
local: string
type: 'import' | 'global'
}

const resolveVitestFn = (
context: TSESLint.RuleContext<string, unknown[]>,
node: TSESTree.Node,
node: TSESTree.CallExpression,
identifier: string
): ResolvedVitestFnType | null => {
): ResolvedVitestFn | null => {
const scope = context.sourceCode.getScope
? context.sourceCode.getScope(node)
: context.getScope()
Expand All @@ -328,6 +322,13 @@ const resolveVitestFn = (
if (maybeImport === 'local')
return null

if (maybeImport === "testContext")
return {
local: identifier,
original: null,
type: "testContext"
}

if (maybeImport) {
if (maybeImport.source === 'vitest') {
return {
Expand Down Expand Up @@ -364,7 +365,7 @@ const resolvePossibleAliasedGlobal = (
export const resolveScope = (
scope: TSESLint.Scope.Scope,
identifier: string
): ImportDetails | 'local' | null => {
): ImportDetails | 'local' | 'testContext' | null => {
let currentScope: TSESLint.Scope.Scope | null = scope

while (currentScope !== null) {
Expand All @@ -373,6 +374,14 @@ export const resolveScope = (
if (ref && ref.defs.length > 0) {
const def = ref.defs[ref.defs.length - 1]

const objectParam = isFunction(def.node) ? def.node.params.find(params => params.type === AST_NODE_TYPES.ObjectPattern ) : undefined
if (objectParam) {
const property = objectParam.properties.find(property => property.type === AST_NODE_TYPES.Property)
const key = property?.key.type === AST_NODE_TYPES.Identifier ? property.key : undefined
if (key?.name === identifier)
return "testContext"
}

const importDetails = describePossibleImportDef(def)

if (importDetails?.local === identifier)
Expand Down
65 changes: 53 additions & 12 deletions tests/prefer-expect-assertions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ruleTester.run(RULE_NAME, rule, {
expect(number).toBeGreaterThan(value);
}
};

it('returns numbers that are greater than two', function () {
expectNumbersToBeGreaterThan(getNumbers(), 2);
});
Expand Down Expand Up @@ -63,6 +63,47 @@ ruleTester.run(RULE_NAME, rule, {
}
]
},
{
code: `
it('my test description', ({ expect }) => {
const a = 1;
const b = 2;

expect(sum(a, b)).toBe(a + b);
})
`,
errors: [
{
messageId: 'haveExpectAssertions',
column: 1,
line: 2,
suggestions: [
{
messageId: 'suggestAddingHasAssertions',
output: `
it('my test description', ({ expect }) => {expect.hasAssertions();
const a = 1;
const b = 2;

expect(sum(a, b)).toBe(a + b);
})
`
},
{
messageId: 'suggestAddingAssertions',
output: `
it('my test description', ({ expect }) => {expect.assertions();
const a = 1;
const b = 2;

expect(sum(a, b)).toBe(a + b);
})
`
}
]
}
],
},
{
code: 'it(\'resolves\', () => expect(staged()).toBe(true));',
errors: [
Expand Down Expand Up @@ -251,12 +292,12 @@ ruleTester.run(RULE_NAME, rule, {
{
code: `it("it1", () => {
expect.hasAssertions();

for (const number of getNumbers()) {
expect(number).toBeGreaterThan(0);
}
});

it("it1", () => {
for (const number of getNumbers()) {
expect(number).toBeGreaterThan(0);
Expand All @@ -273,12 +314,12 @@ ruleTester.run(RULE_NAME, rule, {
messageId: 'suggestAddingHasAssertions',
output: `it("it1", () => {
expect.hasAssertions();

for (const number of getNumbers()) {
expect(number).toBeGreaterThan(0);
}
});

it("it1", () => {expect.hasAssertions();
for (const number of getNumbers()) {
expect(number).toBeGreaterThan(0);
Expand All @@ -289,12 +330,12 @@ ruleTester.run(RULE_NAME, rule, {
messageId: 'suggestAddingAssertions',
output: `it("it1", () => {
expect.hasAssertions();

for (const number of getNumbers()) {
expect(number).toBeGreaterThan(0);
}
});

it("it1", () => {expect.assertions();
for (const number of getNumbers()) {
expect(number).toBeGreaterThan(0);
Expand All @@ -311,7 +352,7 @@ ruleTester.run(RULE_NAME, rule, {
expect(number).toBeGreaterThan(4);
}
});

it("returns numbers that are greater than five", () => {
for (const number of getNumbers()) {
expect(number).toBeGreaterThan(5);
Expand All @@ -332,7 +373,7 @@ ruleTester.run(RULE_NAME, rule, {
expect(number).toBeGreaterThan(4);
}
});

it("returns numbers that are greater than five", () => {
for (const number of getNumbers()) {
expect(number).toBeGreaterThan(5);
Expand All @@ -347,7 +388,7 @@ ruleTester.run(RULE_NAME, rule, {
expect(number).toBeGreaterThan(4);
}
});

it("returns numbers that are greater than five", () => {
for (const number of getNumbers()) {
expect(number).toBeGreaterThan(5);
Expand All @@ -370,7 +411,7 @@ ruleTester.run(RULE_NAME, rule, {
expect(number).toBeGreaterThan(4);
}
});

it("returns numbers that are greater than five", () => {expect.hasAssertions();
for (const number of getNumbers()) {
expect(number).toBeGreaterThan(5);
Expand All @@ -385,7 +426,7 @@ ruleTester.run(RULE_NAME, rule, {
expect(number).toBeGreaterThan(4);
}
});

it("returns numbers that are greater than five", () => {expect.assertions();
for (const number of getNumbers()) {
expect(number).toBeGreaterThan(5);
Expand Down