diff --git a/src/rules/__tests__/valid-expect.test.ts b/src/rules/__tests__/valid-expect.test.ts index 78f9cc964..10434a43b 100644 --- a/src/rules/__tests__/valid-expect.test.ts +++ b/src/rules/__tests__/valid-expect.test.ts @@ -74,6 +74,35 @@ ruleTester.run('valid-expect', rule, { return expect(functionReturningAPromise()).resolves.toEqual(1).then(() => expect(Promise.resolve(2)).resolves.toBe(1)); }); `, + dedent` + expect.extend({ + toResolve(obj) { + return this.isNot + ? expect(obj).toBe(true) + : expect(obj).resolves.not.toThrow(); + } + }); + `, + dedent` + expect.extend({ + toResolve(obj) { + return this.isNot + ? expect(obj).resolves.not.toThrow() + : expect(obj).toBe(true); + } + }); + `, + dedent` + expect.extend({ + toResolve(obj) { + return this.isNot + ? expect(obj).toBe(true) + : anotherCondition + ? expect(obj).resolves.not.toThrow() + : expect(obj).toBe(false) + } + }); + `, { code: 'expect(1).toBe(2);', options: [{ maxArgs: 2 }], @@ -369,6 +398,63 @@ ruleTester.run('valid-expect', rule, { ], }, + { + code: dedent` + expect.extend({ + toResolve(obj) { + this.isNot + ? expect(obj).toBe(true) + : expect(obj).resolves.not.toThrow(); + } + }); + `, + errors: [ + { + column: 9, + endColumn: 43, + messageId: 'asyncMustBeAwaited', + }, + ], + }, + { + code: dedent` + expect.extend({ + toResolve(obj) { + this.isNot + ? expect(obj).resolves.not.toThrow() + : expect(obj).toBe(true); + } + }); + `, + errors: [ + { + column: 9, + endColumn: 43, + messageId: 'asyncMustBeAwaited', + }, + ], + }, + { + code: dedent` + expect.extend({ + toResolve(obj) { + this.isNot + ? expect(obj).toBe(true) + : anotherCondition + ? expect(obj).resolves.not.toThrow() + : expect(obj).toBe(false) + } + }); + `, + errors: [ + { + column: 9, + endColumn: 43, + messageId: 'asyncMustBeAwaited', + }, + ], + }, + // expect().resolves { code: diff --git a/src/rules/valid-expect.ts b/src/rules/valid-expect.ts index 2869ecc64..f0118b987 100644 --- a/src/rules/valid-expect.ts +++ b/src/rules/valid-expect.ts @@ -80,14 +80,23 @@ const isAcceptableReturnNode = ( node: TSESTree.Node, allowReturn: boolean, ): node is + | TSESTree.ConditionalExpression | TSESTree.ArrowFunctionExpression | TSESTree.AwaitExpression - | TSESTree.ReturnStatement => - (allowReturn && node.type === AST_NODE_TYPES.ReturnStatement) || - [ + | TSESTree.ReturnStatement => { + if (allowReturn && node.type === AST_NODE_TYPES.ReturnStatement) { + return true; + } + + if (node.type === AST_NODE_TYPES.ConditionalExpression && node.parent) { + return isAcceptableReturnNode(node.parent, allowReturn); + } + + return [ AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.AwaitExpression, ].includes(node.type); +}; const isNoAssertionsParentNode = (node: TSESTree.Node): boolean => node.type === AST_NODE_TYPES.ExpressionStatement ||