From 744d4f6fa5685e0c87062cc867ecadbad9b2e06c Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Sun, 7 Apr 2024 11:03:18 +1200 Subject: [PATCH] feat: support providing aliases for `@jest/globals` package (#1543) --- README.md | 21 ++ .../utils/__tests__/parseJestFnCall.test.ts | 192 ++++++++++++++++++ src/rules/utils/parseJestFnCall.ts | 10 +- 3 files changed, 220 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0fbc1ca73..a2a3b3d26 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,27 @@ You can tell this plugin about any global Jests you have aliased using the } ``` +#### Aliased `@jest/globals` + +You can tell this plugin to treat a different package as the source of Jest +globals using the `globalPackage` setting: + +```json +{ + "settings": { + "jest": { + "globalPackage": "bun:test" + } + } +} +``` + +> [!WARNING] +> +> While this can be used to apply rules when using alternative testing libraries +> and frameworks like `bun`, `vitest` and `node`, there's no guarantee the +> semantics this plugin assumes will hold outside of Jest + ### Running rules only on test-related files The rules provided by this plugin assume that the files they are checking are diff --git a/src/rules/utils/__tests__/parseJestFnCall.test.ts b/src/rules/utils/__tests__/parseJestFnCall.test.ts index 28c59abe9..ab077af86 100644 --- a/src/rules/utils/__tests__/parseJestFnCall.test.ts +++ b/src/rules/utils/__tests__/parseJestFnCall.test.ts @@ -737,6 +737,198 @@ ruleTester.run('global aliases', rule, { ], }); +ruleTester.run('global package source', rule, { + valid: [ + { + code: dedent` + import { expect } from 'bun:test' + + expect(x).toBe(y); + `, + parserOptions: { sourceType: 'module' }, + settings: { jest: { globalPackage: '@jest/globals' } }, + }, + { + code: dedent` + const { it } = require('@jest/globals'); + + it('is not considered a test function', () => {}); + `, + parserOptions: { sourceType: 'script' }, + settings: { jest: { globalPackage: 'bun:test' } }, + }, + { + code: dedent` + const { fn: it } = require('bun:test'); + + it('is not considered a test function', () => {}); + `, + parserOptions: { sourceType: 'script' }, + settings: { jest: { globalPackage: 'bun:test' } }, + }, + { + code: dedent` + import { it } from '@jest/globals'; + + it('is not considered a test function', () => {}); + `, + parserOptions: { sourceType: 'module' }, + settings: { jest: { globalPackage: 'bun:test' } }, + }, + { + code: dedent` + import { fn as it } from 'bun:test'; + + it('is not considered a test function', () => {}); + `, + parserOptions: { sourceType: 'module' }, + settings: { jest: { globalPackage: 'bun:test' } }, + }, + ], + invalid: [ + { + code: 'expect(x).toBe(y);', + parserOptions: { sourceType: 'script' }, + errors: [ + { + messageId: 'details' as const, + data: expectedParsedJestFnCallResultData({ + name: 'expect', + type: 'expect', + head: { + original: null, + local: 'expect', + type: 'global', + node: 'expect', + }, + members: ['toBe'], + }), + column: 1, + line: 1, + }, + ], + settings: { jest: { globalPackage: 'bun:test' } }, + }, + { + code: dedent` + import { describe, expect, it } from 'bun:test' + + describe('some tests', () => { + it('ensures something', () => { + expect.assertions(); + }); + }); + `, + parserOptions: { sourceType: 'module' }, + errors: [ + { + messageId: 'details' as const, + data: expectedParsedJestFnCallResultData({ + name: 'describe', + type: 'describe', + head: { + original: 'describe', + local: 'describe', + type: 'import', + node: 'describe', + }, + members: [], + }), + column: 1, + line: 3, + }, + { + messageId: 'details' as const, + data: expectedParsedJestFnCallResultData({ + name: 'it', + type: 'test', + head: { + original: 'it', + local: 'it', + type: 'import', + node: 'it', + }, + members: [], + }), + column: 3, + line: 4, + }, + { + messageId: 'details' as const, + data: expectedParsedJestFnCallResultData({ + name: 'expect', + type: 'expect', + head: { + original: 'expect', + local: 'expect', + type: 'import', + node: 'expect', + }, + members: ['assertions'], + }), + column: 5, + line: 5, + }, + ], + settings: { jest: { globalPackage: 'bun:test' } }, + }, + { + code: dedent` + import { expect } from 'bun:test' + + expect(x).not.toBe(y); + `, + parserOptions: { sourceType: 'module' }, + errors: [ + { + messageId: 'details' as const, + data: expectedParsedJestFnCallResultData({ + name: 'expect', + type: 'expect', + head: { + original: 'expect', + local: 'expect', + type: 'import', + node: 'expect', + }, + members: ['not', 'toBe'], + }), + column: 1, + line: 3, + }, + ], + settings: { jest: { globalPackage: 'bun:test' } }, + }, + { + code: 'context("when there is an error", () => {})', + errors: [ + { + messageId: 'details' as const, + data: expectedParsedJestFnCallResultData({ + name: 'describe', + type: 'describe', + head: { + original: 'describe', + local: 'context', + type: 'global', + node: 'context', + }, + members: [], + }), + column: 1, + line: 1, + }, + ], + settings: { + jest: { + globalPackage: 'bun:test', + globalAliases: { describe: ['context'] }, + }, + }, + }, + ], +}); + ruleTester.run('typescript', rule, { valid: [ { diff --git a/src/rules/utils/parseJestFnCall.ts b/src/rules/utils/parseJestFnCall.ts index 0f5d65ebf..be70caa1e 100644 --- a/src/rules/utils/parseJestFnCall.ts +++ b/src/rules/utils/parseJestFnCall.ts @@ -188,6 +188,7 @@ const ValidJestFnCallChains = [ interface SharedConfigurationSettings { jest?: { globalAliases?: Record; + globalPackage?: string; version?: number | string; }; } @@ -584,9 +585,12 @@ const resolveToJestFn = ( } if (maybeImport) { - // the identifier is imported from @jest/globals, - // so return the original import name - if (maybeImport.source === '@jest/globals') { + const globalPackage = + (context.settings as SharedConfigurationSettings).jest?.globalPackage ?? + '@jest/globals'; + + // the identifier is imported from our global package so return the original import name + if (maybeImport.source === globalPackage) { return { original: maybeImport.imported, local: maybeImport.local,