@@ -2,11 +2,49 @@ import {
22 ASTUtils ,
33 AST_NODE_TYPES ,
44 ESLintUtils ,
5+ ParserServices ,
6+ TSESLint ,
57 TSESTree ,
68} from '@typescript-eslint/experimental-utils' ;
79import * as ts from 'typescript' ;
8- import { createRule } from './utils' ;
10+ import { createRule , isExpectCall , parseExpectCall } from './utils' ;
911
12+ //------------------------------------------------------------------------------
13+ // eslint-plugin-jest extension
14+ //------------------------------------------------------------------------------
15+
16+ const getParserServices = (
17+ context : Readonly < TSESLint . RuleContext < MessageIds , Options > > ,
18+ ) : ParserServices | null => {
19+ try {
20+ return ESLintUtils . getParserServices ( context ) ;
21+ } catch {
22+ return null ;
23+ }
24+ } ;
25+
26+ const toThrowMatchers = [
27+ 'toThrow' ,
28+ 'toThrowError' ,
29+ 'toThrowErrorMatchingSnapshot' ,
30+ 'toThrowErrorMatchingInlineSnapshot' ,
31+ ] ;
32+
33+ const isJestExpectCall = ( node : TSESTree . CallExpression ) => {
34+ if ( ! isExpectCall ( node ) ) {
35+ return false ;
36+ }
37+
38+ const { matcher } = parseExpectCall ( node ) ;
39+
40+ return ! toThrowMatchers . includes ( matcher ?. name ?? '' ) ;
41+ } ;
42+
43+ //------------------------------------------------------------------------------
44+ // Inlining of external dependencies
45+ //------------------------------------------------------------------------------
46+
47+ /* istanbul ignore next */
1048const tsutils = {
1149 hasModifier (
1250 modifiers : ts . ModifiersArray | undefined ,
@@ -28,7 +66,6 @@ const tsutils = {
2866
2967const util = {
3068 createRule,
31- getParserServices : ESLintUtils . getParserServices ,
3269 isIdentifier : ASTUtils . isIdentifier ,
3370} ;
3471
@@ -108,6 +145,7 @@ const SUPPORTED_GLOBALS = [
108145 'Intl' ,
109146] as const ;
110147const nativelyBoundMembers = SUPPORTED_GLOBALS . map ( namespace => {
148+ /* istanbul ignore if */
111149 if ( ! ( namespace in global ) ) {
112150 // node.js might not have namespaces like Intl depending on compilation options
113151 // https://nodejs.org/api/intl.html#intl_options_for_building_node_js
@@ -156,7 +194,7 @@ export default util.createRule<Options, MessageIds>({
156194 category : 'Best Practices' ,
157195 description :
158196 'Enforces unbound methods are called with their expected scope' ,
159- recommended : 'error' ,
197+ recommended : false ,
160198 requiresTypeChecking : true ,
161199 } ,
162200 messages : {
@@ -182,14 +220,33 @@ export default util.createRule<Options, MessageIds>({
182220 } ,
183221 ] ,
184222 create ( context , [ { ignoreStatic } ] ) {
185- const parserServices = util . getParserServices ( context ) ;
223+ const parserServices = getParserServices ( context ) ;
224+
225+ if ( ! parserServices ) {
226+ return { } ;
227+ }
228+
186229 const checker = parserServices . program . getTypeChecker ( ) ;
187230 const currentSourceFile = parserServices . program . getSourceFile (
188231 context . getFilename ( ) ,
189232 ) ;
190233
234+ let inExpectCall = false ;
235+
191236 return {
237+ CallExpression ( node : TSESTree . CallExpression ) : void {
238+ inExpectCall = isJestExpectCall ( node ) ;
239+ } ,
240+ 'CallExpression:exit' ( node : TSESTree . CallExpression ) : void {
241+ if ( inExpectCall && isJestExpectCall ( node ) ) {
242+ inExpectCall = false ;
243+ }
244+ } ,
192245 MemberExpression ( node : TSESTree . MemberExpression ) : void {
246+ if ( inExpectCall ) {
247+ return ;
248+ }
249+
193250 if ( isSafeUse ( node ) ) {
194251 return ;
195252 }
@@ -233,6 +290,7 @@ export default util.createRule<Options, MessageIds>({
233290 rightSymbol && isNotImported ( rightSymbol , currentSourceFile ) ;
234291
235292 idNode . properties . forEach ( property => {
293+ /* istanbul ignore else */
236294 if (
237295 property . type === AST_NODE_TYPES . Property &&
238296 property . key . type === AST_NODE_TYPES . Identifier
0 commit comments