Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jest-community/eslint-plugin-jest
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v28.3.0
Choose a base ref
...
head repository: jest-community/eslint-plugin-jest
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v28.4.0
Choose a head ref
  • 7 commits
  • 14 files changed
  • 5 contributors

Commits on Apr 29, 2024

  1. chore(deps): lock file maintenance

    renovate[bot] committed Apr 29, 2024
    Copy the full SHA
    8001fe7 View commit details

Commits on May 2, 2024

  1. chore(deps): update yarn to v3.8.2 (#1575)

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
    renovate[bot] authored May 2, 2024
    Copy the full SHA
    df3202f View commit details
  2. refactor(prefer-importing-jest-globals): use AST_NODE_TYPES constan…

    …t instead of string literal (#1576)
    G-Rath authored May 2, 2024
    Copy the full SHA
    aac5f03 View commit details
  3. refactor(prefer-lowercase-title): remove unneeded cast (#1577)

    Co-authored-by: Hasegawa-Yukihiro <y.h.baskeeee@icloud.com>
    G-Rath and y-hsgw authored May 2, 2024
    Copy the full SHA
    6c1f921 View commit details

Commits on May 3, 2024

  1. refactor: remove unneeded as consts (#1578)

    y-hsgw authored May 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f47cc3c View commit details
  2. feat(valid-expect): supporting automatically fixing missing await i…

    …n some cases (#1574)
    
    * feat: add 'fixable' to valid-expect
    
    * test: add test to valid-expect
    
    * docs: update doc for valid-expect
    
    * docs: added additional note
    
    * fix: docs
    
    * docs: using fancy alerts
    
    * fix: format
    y-hsgw authored May 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a407098 View commit details
  3. chore(release): 28.4.0 [skip ci]

    # [28.4.0](v28.3.0...v28.4.0) (2024-05-03)
    
    ### Features
    
    * **valid-expect:** supporting automatically fixing missing `await` in some cases ([#1574](#1574)) ([a407098](a407098))
    semantic-release-bot committed May 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    27f7e74 View commit details
444 changes: 222 additions & 222 deletions .yarn/releases/yarn-3.8.1.cjs → .yarn/releases/yarn-3.8.2.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -6,4 +6,4 @@ plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: '@yarnpkg/plugin-interactive-tools'

yarnPath: .yarn/releases/yarn-3.8.1.cjs
yarnPath: .yarn/releases/yarn-3.8.2.cjs
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# [28.4.0](https://github.com/jest-community/eslint-plugin-jest/compare/v28.3.0...v28.4.0) (2024-05-03)


### Features

* **valid-expect:** supporting automatically fixing missing `await` in some cases ([#1574](https://github.com/jest-community/eslint-plugin-jest/issues/1574)) ([a407098](https://github.com/jest-community/eslint-plugin-jest/commit/a40709833cd12a87b746ddf2e26a10af838bca0a))

# [28.3.0](https://github.com/jest-community/eslint-plugin-jest/compare/v28.2.0...v28.3.0) (2024-04-27)


2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -363,7 +363,7 @@ set to warn in.\
| [require-to-throw-message](docs/rules/require-to-throw-message.md) | Require a message for `toThrow()` | | | | |
| [require-top-level-describe](docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `describe` block | | | | |
| [valid-describe-callback](docs/rules/valid-describe-callback.md) | Enforce valid `describe()` callback || | | |
| [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage || | | |
| [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage || | 🔧 | |
| [valid-expect-in-promise](docs/rules/valid-expect-in-promise.md) | Require promises that have expectations in their chain to be valid || | | |
| [valid-title](docs/rules/valid-title.md) | Enforce valid titles || | 🔧 | |

6 changes: 6 additions & 0 deletions docs/rules/valid-expect.md
Original file line number Diff line number Diff line change
@@ -3,8 +3,14 @@
💼 This rule is enabled in the ✅ `recommended`
[config](https://github.com/jest-community/eslint-plugin-jest/blob/main/README.md#shareable-configurations).

🔧 This rule is automatically fixable by the
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

<!-- end auto-generated rule header -->

> [!NOTE] Test function will be fixed if it is async and does not have await in
> the async assertion.
Ensure `expect()` is called with a single argument and there is an actual
expectation made.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-jest",
"version": "28.3.0",
"version": "28.4.0",
"description": "ESLint rules for Jest",
"keywords": [
"eslint",
@@ -124,7 +124,7 @@
"optional": true
}
},
"packageManager": "yarn@3.8.1",
"packageManager": "yarn@3.8.2",
"engines": {
"node": "^16.10.0 || ^18.12.0 || >=20.0.0"
},
69 changes: 69 additions & 0 deletions src/rules/__tests__/valid-expect.test.ts
Original file line number Diff line number Diff line change
@@ -571,6 +571,8 @@ ruleTester.run('valid-expect', rule, {
// usages in async function
{
code: 'test("valid-expect", async () => { expect(Promise.resolve(2)).resolves.toBeDefined(); });',
output:
'test("valid-expect", async () => { await expect(Promise.resolve(2)).resolves.toBeDefined(); });',
errors: [
{
column: 36,
@@ -582,6 +584,8 @@ ruleTester.run('valid-expect', rule, {
},
{
code: 'test("valid-expect", async () => { expect(Promise.resolve(2)).resolves.not.toBeDefined(); });',
output:
'test("valid-expect", async () => { await expect(Promise.resolve(2)).resolves.not.toBeDefined(); });',
errors: [
{
column: 36,
@@ -621,6 +625,12 @@ ruleTester.run('valid-expect', rule, {
expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
output: dedent`
test("valid-expect", async () => {
await expect(Promise.resolve(2)).resolves.not.toBeDefined();
await expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
errors: [
{
line: 2,
@@ -646,6 +656,12 @@ ruleTester.run('valid-expect', rule, {
expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
output: dedent`
test("valid-expect", async () => {
await expect(Promise.resolve(2)).resolves.not.toBeDefined();
await expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
errors: [
{
line: 3,
@@ -667,6 +683,12 @@ ruleTester.run('valid-expect', rule, {
return expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
output: dedent`
test("valid-expect", async () => {
await expect(Promise.resolve(2)).resolves.not.toBeDefined();
await expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
options: [{ alwaysAwait: true }],
errors: [
{
@@ -691,6 +713,12 @@ ruleTester.run('valid-expect', rule, {
return expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
output: dedent`
test("valid-expect", async () => {
await expect(Promise.resolve(2)).resolves.not.toBeDefined();
return expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
errors: [
{
line: 2,
@@ -709,6 +737,12 @@ ruleTester.run('valid-expect', rule, {
return expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
output: dedent`
test("valid-expect", async () => {
await expect(Promise.resolve(2)).resolves.not.toBeDefined();
await expect(Promise.resolve(1)).rejects.toBeDefined();
});
`,
options: [{ alwaysAwait: true }],
errors: [
{
@@ -726,6 +760,12 @@ ruleTester.run('valid-expect', rule, {
return expect(Promise.resolve(1)).toReject();
});
`,
output: dedent`
test("valid-expect", async () => {
await expect(Promise.resolve(2)).toResolve();
await expect(Promise.resolve(1)).toReject();
});
`,
options: [{ alwaysAwait: true }],
errors: [
{
@@ -771,6 +811,27 @@ ruleTester.run('valid-expect', rule, {
},
],
},
{
code: dedent`
test("valid-expect", async () => {
Promise.reject(expect(Promise.resolve(2)).resolves.not.toBeDefined());
});
`,
output: dedent`
test("valid-expect", async () => {
await Promise.reject(expect(Promise.resolve(2)).resolves.not.toBeDefined());
});
`,
errors: [
{
line: 2,
column: 3,
endColumn: 72,
messageId: 'promisesWithAsyncAssertionsMustBeAwaited',
data: { orReturned: ' or returned' },
},
],
},
{
code: dedent`
test("valid-expect", () => {
@@ -961,6 +1022,14 @@ ruleTester.run('valid-expect', rule, {
});
});
`,
output: dedent`
test("valid-expect", () => {
return expect(functionReturningAPromise()).resolves.toEqual(1).then(async () => {
await expect(Promise.resolve(2)).resolves.toBe(1);
await expect(Promise.resolve(4)).resolves.toBe(4);
});
});
`,
errors: [
{
line: 4,
2 changes: 1 addition & 1 deletion src/rules/prefer-importing-jest-globals.ts
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ export default createRule({
return {
ImportDeclaration(node: TSESTree.ImportDeclaration) {
node.specifiers.forEach(specifier => {
if (specifier.type === 'ImportSpecifier') {
if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {
importedFunctionsWithSource[specifier.local.name] =
node.source.value;
}
4 changes: 2 additions & 2 deletions src/rules/prefer-lowercase-title.ts
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@ export default createRule<
additionalProperties: false,
},
],
} as const,
},
defaultOptions: [
{ ignore: [], allowedPrefixes: [], ignoreTopLevelDescribe: false },
],
@@ -134,7 +134,7 @@ export default createRule<
if (
!firstCharacter ||
firstCharacter === firstCharacter.toLowerCase() ||
ignores.includes(jestFnCall.name as IgnorableFunctionExpressions)
ignores.includes(jestFnCall.name)
) {
return;
}
12 changes: 6 additions & 6 deletions src/rules/utils/__tests__/detectJestVersion.test.ts
Original file line number Diff line number Diff line change
@@ -115,7 +115,7 @@ describe('detectJestVersion', () => {
it('finds the correct version', () => {
const projectDir = setupFakeProject({
'package.json': { name: 'simple-project' },
[`node_modules/${relativePathToFn}` as const]: compiledFn,
[`node_modules/${relativePathToFn}`]: compiledFn,
'node_modules/jest/package.json': {
name: 'jest',
version: '21.0.0',
@@ -133,7 +133,7 @@ describe('detectJestVersion', () => {
it('finds the correct version', () => {
const projectDir = setupFakeProject({
'package.json': { name: 'mono-repo' },
[`node_modules/${relativePathToFn}` as const]: compiledFn,
[`node_modules/${relativePathToFn}`]: compiledFn,
'node_modules/jest/package.json': {
name: 'jest',
version: '19.0.0',
@@ -153,13 +153,13 @@ describe('detectJestVersion', () => {
it('finds the correct versions', () => {
const projectDir = setupFakeProject({
'backend/package.json': { name: 'package-a' },
[`backend/node_modules/${relativePathToFn}` as const]: compiledFn,
[`backend/node_modules/${relativePathToFn}`]: compiledFn,
'backend/node_modules/jest/package.json': {
name: 'jest',
version: '24.0.0',
},
'frontend/package.json': { name: 'package-b' },
[`frontend/node_modules/${relativePathToFn}` as const]: compiledFn,
[`frontend/node_modules/${relativePathToFn}`]: compiledFn,
'frontend/node_modules/jest/package.json': {
name: 'jest',
version: '15.0.0',
@@ -184,7 +184,7 @@ describe('detectJestVersion', () => {
it('throws an error', () => {
const projectDir = setupFakeProject({
'package.json': { name: 'no-jest' },
[`node_modules/${relativePathToFn}` as const]: compiledFn,
[`node_modules/${relativePathToFn}`]: compiledFn,
'node_modules/pack/package.json': { name: 'pack' },
});

@@ -199,7 +199,7 @@ describe('detectJestVersion', () => {
it('uses the cached version', () => {
const projectDir = setupFakeProject({
'package.json': { name: 'no-jest' },
[`node_modules/${relativePathToFn}` as const]: compiledFn,
[`node_modules/${relativePathToFn}`]: compiledFn,
'node_modules/jest/package.json': { name: 'jest', version: '26.0.0' },
});

46 changes: 23 additions & 23 deletions src/rules/utils/__tests__/parseJestFnCall.test.ts
Original file line number Diff line number Diff line change
@@ -194,7 +194,7 @@ ruleTester.run('expect', rule, {
parserOptions: { sourceType: 'script' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -220,7 +220,7 @@ ruleTester.run('expect', rule, {
parserOptions: { sourceType: 'module' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -246,7 +246,7 @@ ruleTester.run('expect', rule, {
parserOptions: { sourceType: 'module' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -272,7 +272,7 @@ ruleTester.run('expect', rule, {
parserOptions: { sourceType: 'module' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -298,7 +298,7 @@ ruleTester.run('expect', rule, {
parserOptions: { sourceType: 'module' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -327,7 +327,7 @@ ruleTester.run('expect', rule, {
parserOptions: { sourceType: 'module' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -343,7 +343,7 @@ ruleTester.run('expect', rule, {
line: 3,
},
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -359,7 +359,7 @@ ruleTester.run('expect', rule, {
line: 4,
},
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -375,7 +375,7 @@ ruleTester.run('expect', rule, {
line: 5,
},
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -493,7 +493,7 @@ if (eslintVersion >= 8) {
parserOptions: { sourceType: 'module', ecmaVersion: 2022 },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'it',
type: 'test',
@@ -519,7 +519,7 @@ if (eslintVersion >= 8) {
parserOptions: { sourceType: 'module', ecmaVersion: 2022 },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'it',
type: 'test',
@@ -630,7 +630,7 @@ ruleTester.run('global aliases', rule, {
code: 'context("when there is an error", () => {})',
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'describe',
type: 'describe',
@@ -652,7 +652,7 @@ ruleTester.run('global aliases', rule, {
code: 'context.skip("when there is an error", () => {})',
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'describe',
type: 'describe',
@@ -677,7 +677,7 @@ ruleTester.run('global aliases', rule, {
`,
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'xdescribe',
type: 'describe',
@@ -703,7 +703,7 @@ ruleTester.run('global aliases', rule, {
`,
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'describe',
type: 'describe',
@@ -719,7 +719,7 @@ ruleTester.run('global aliases', rule, {
line: 1,
},
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'describe',
type: 'describe',
@@ -794,7 +794,7 @@ ruleTester.run('global package source', rule, {
parserOptions: { sourceType: 'script' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -825,7 +825,7 @@ ruleTester.run('global package source', rule, {
parserOptions: { sourceType: 'module' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'describe',
type: 'describe',
@@ -841,7 +841,7 @@ ruleTester.run('global package source', rule, {
line: 3,
},
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'it',
type: 'test',
@@ -857,7 +857,7 @@ ruleTester.run('global package source', rule, {
line: 4,
},
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -884,7 +884,7 @@ ruleTester.run('global package source', rule, {
parserOptions: { sourceType: 'module' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'expect',
type: 'expect',
@@ -906,7 +906,7 @@ ruleTester.run('global package source', rule, {
code: 'context("when there is an error", () => {})',
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'describe',
type: 'describe',
@@ -1013,7 +1013,7 @@ ruleTester.run('typescript', rule, {
parserOptions: { sourceType: 'module' },
errors: [
{
messageId: 'details' as const,
messageId: 'details',
data: expectedParsedJestFnCallResultData({
name: 'it',
type: 'test',
34 changes: 34 additions & 0 deletions src/rules/valid-expect.ts
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@ import {
ModifierName,
createRule,
getAccessorValue,
getSourceCode,
isFunction,
isSupportedAccessor,
parseJestFnCallWithReason,
} from './utils';
@@ -48,6 +50,18 @@ const findPromiseCallExpressionNode = (node: TSESTree.Node) =>
? getPromiseCallExpressionNode(node.parent)
: null;

const findFirstAsyncFunction = ({
parent,
}: TSESTree.Node): TSESTree.Node | null => {
if (!parent) {
return null;
}

return isFunction(parent) && parent.async
? parent
: findFirstAsyncFunction(parent);
};

const getParentIfThenified = (node: TSESTree.Node): TSESTree.Node => {
const grandParentNode = node.parent?.parent;

@@ -127,6 +141,7 @@ export default createRule<[Options], MessageIds>({
promisesWithAsyncAssertionsMustBeAwaited:
'Promises which return async assertions must be awaited{{ orReturned }}',
},
fixable: 'code',
type: 'suggestion',
schema: [
{
@@ -339,6 +354,25 @@ export default createRule<[Options], MessageIds>({
? 'asyncMustBeAwaited'
: 'promisesWithAsyncAssertionsMustBeAwaited',
node,
fix(fixer) {
if (!findFirstAsyncFunction(finalNode)) {
return [];
}
const returnStatement =
finalNode.parent?.type === AST_NODE_TYPES.ReturnStatement
? finalNode.parent
: null;

if (alwaysAwait && returnStatement) {
const sourceCodeText =
getSourceCode(context).getText(returnStatement);
const replacedText = sourceCodeText.replace('return', 'await');

return fixer.replaceText(returnStatement, replacedText);
}

return fixer.insertTextBefore(finalNode, 'await ');
},
});

if (isParentArrayExpression) {
2 changes: 1 addition & 1 deletion src/rules/valid-title.ts
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@ const MatcherAndMessageSchema: JSONSchema.JSONSchema4 = {
minItems: 1,
maxItems: 2,
additionalItems: false,
} as const;
};

type MatcherGroups = 'describe' | 'test' | 'it';

359 changes: 159 additions & 200 deletions yarn.lock

Large diffs are not rendered by default.