From 090fd9163ef33086365ed722e50b7c4634d3c842 Mon Sep 17 00:00:00 2001 From: Erin Zimmer Date: Fri, 30 Aug 2024 13:16:14 +1000 Subject: [PATCH] fix(prefer-importing-jest-globals): ensure imports aren't inserted in the middle of a statement --- .../prefer-importing-jest-globals.test.ts | 57 ++++++++++++++++++- src/rules/prefer-importing-jest-globals.ts | 22 ++++++- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/rules/__tests__/prefer-importing-jest-globals.test.ts b/src/rules/__tests__/prefer-importing-jest-globals.test.ts index 19bc24b8b..3d853d09b 100644 --- a/src/rules/__tests__/prefer-importing-jest-globals.test.ts +++ b/src/rules/__tests__/prefer-importing-jest-globals.test.ts @@ -228,8 +228,8 @@ ruleTester.run('prefer-importing-jest-globals', rule, { }); `, output: dedent` - import { pending } from 'actions'; import { describe, test } from '@jest/globals'; + import { pending } from 'actions'; describe('foo', () => { test.each(['hello', 'world'])("%s", (a) => {}); }); @@ -546,5 +546,60 @@ ruleTester.run('prefer-importing-jest-globals', rule, { }, ], }, + { + code: dedent` + console.log('hello'); + const onClick = jest.fn(); + describe("suite", () => { + test("foo"); + expect(onClick).toHaveBeenCalled(); + }) + `, + output: dedent` + console.log('hello'); + const { describe, expect, jest, test } = require('@jest/globals'); + const onClick = jest.fn(); + describe("suite", () => { + test("foo"); + expect(onClick).toHaveBeenCalled(); + }) + `, + errors: [ + { + endColumn: 21, + column: 17, + line: 2, + messageId: 'preferImportingJestGlobal', + }, + ], + }, + { + code: dedent` + console.log('hello'); + const onClick = jest.fn(); + describe("suite", () => { + test("foo"); + expect(onClick).toHaveBeenCalled(); + }) + `, + output: dedent` + import { describe, expect, jest, test } from '@jest/globals'; + console.log('hello'); + const onClick = jest.fn(); + describe("suite", () => { + test("foo"); + expect(onClick).toHaveBeenCalled(); + }) + `, + parserOptions: { sourceType: 'module' }, + errors: [ + { + endColumn: 21, + column: 17, + line: 2, + messageId: 'preferImportingJestGlobal', + }, + ], + }, ], }); diff --git a/src/rules/prefer-importing-jest-globals.ts b/src/rules/prefer-importing-jest-globals.ts index 9cce2d213..532a4bfaf 100644 --- a/src/rules/prefer-importing-jest-globals.ts +++ b/src/rules/prefer-importing-jest-globals.ts @@ -21,6 +21,22 @@ const createFixerImports = ( : `const { ${allImportsFormatted} } = require('@jest/globals');`; }; +const findInsertionPoint = (reportingNode: TSESTree.Node) => { + let currentNode = reportingNode; + + while ( + currentNode.parent && + currentNode.parent.type !== AST_NODE_TYPES.Program && + currentNode.parent.type !== AST_NODE_TYPES.VariableDeclaration + ) { + currentNode = currentNode.parent; + } + + return currentNode.parent?.type === AST_NODE_TYPES.VariableDeclaration + ? currentNode.parent + : reportingNode; +}; + const allJestFnTypes: JestFnType[] = [ 'hook', 'describe', @@ -158,8 +174,12 @@ export default createRule({ ); if (requireNode?.type !== AST_NODE_TYPES.VariableDeclaration) { + const insertBeforeNode = isModule + ? firstNode + : findInsertionPoint(reportingNode); + return fixer.insertTextBefore( - reportingNode, + insertBeforeNode, `${createFixerImports(isModule, functionsToImport)}\n`, ); }