@@ -377,20 +377,59 @@ module.exports = {
377
377
} ,
378
378
379
379
/**
380
- * Performs static analysis on an AST to try to find test cases
380
+ * Extracts the body of a function if the given node is a function
381
+ *
382
+ * @param {ASTNode } node
383
+ * @returns {ExpressionStatement[] }
384
+ */
385
+ extractFunctionBody ( node ) {
386
+ if (
387
+ node . type === 'ArrowFunctionExpression' ||
388
+ node . type === 'FunctionExpression'
389
+ ) {
390
+ if ( node . body . type === 'BlockStatement' ) {
391
+ return node . body . body ;
392
+ }
393
+
394
+ return [ node . body ] ;
395
+ }
396
+
397
+ return [ ] ;
398
+ } ,
399
+
400
+ /**
401
+ * Checks the given statements for possible test info
402
+ *
381
403
* @param {RuleContext } context The `context` variable for the source file itself
382
- * @param {ASTNode } ast The `Program` node for the file.
383
- * @returns {object } An object with `valid` and `invalid` keys containing a list of AST nodes corresponding to tests
404
+ * @param {ASTNode[] } statements The statements to check
405
+ * @param {Set<ASTNode> } variableIdentifiers
406
+ * @returns {CallExpression[] }
384
407
*/
385
- getTestInfo ( context , ast ) {
408
+ checkStatementsForTestInfo (
409
+ context ,
410
+ statements ,
411
+ variableIdentifiers = new Set ( )
412
+ ) {
386
413
const runCalls = [ ] ;
387
- const variableIdentifiers = new Set ( ) ;
388
414
389
- ast . body . forEach ( ( statement ) => {
415
+ for ( const statement of statements ) {
390
416
if ( statement . type === 'VariableDeclaration' ) {
391
- statement . declarations . forEach ( ( declarator ) => {
417
+ for ( const declarator of statement . declarations ) {
418
+ if ( ! declarator . init ) {
419
+ continue ;
420
+ }
421
+
422
+ const extracted = module . exports . extractFunctionBody ( declarator . init ) ;
423
+
424
+ runCalls . push (
425
+ ...module . exports . checkStatementsForTestInfo (
426
+ context ,
427
+ extracted ,
428
+ variableIdentifiers
429
+ )
430
+ ) ;
431
+
392
432
if (
393
- declarator . init &&
394
433
isRuleTesterConstruction ( declarator . init ) &&
395
434
declarator . id . type === 'Identifier'
396
435
) {
@@ -400,21 +439,82 @@ module.exports = {
400
439
. forEach ( ( ref ) => variableIdentifiers . add ( ref . identifier ) ) ;
401
440
} ) ;
402
441
}
403
- } ) ;
442
+ }
443
+ }
444
+
445
+ if ( statement . type === 'FunctionDeclaration' ) {
446
+ runCalls . push (
447
+ ...module . exports . checkStatementsForTestInfo (
448
+ context ,
449
+ statement . body . body ,
450
+ variableIdentifiers
451
+ )
452
+ ) ;
453
+ }
454
+
455
+ if ( statement . type === 'IfStatement' ) {
456
+ const body =
457
+ statement . consequent . type === 'BlockStatement'
458
+ ? statement . consequent . body
459
+ : [ statement . consequent ] ;
460
+
461
+ runCalls . push (
462
+ ...module . exports . checkStatementsForTestInfo (
463
+ context ,
464
+ body ,
465
+ variableIdentifiers
466
+ )
467
+ ) ;
468
+
469
+ continue ;
470
+ }
471
+
472
+ const expression =
473
+ statement . type === 'ExpressionStatement'
474
+ ? statement . expression
475
+ : statement ;
476
+
477
+ if ( expression . type !== 'CallExpression' ) {
478
+ continue ;
479
+ }
480
+
481
+ for ( const arg of expression . arguments ) {
482
+ const extracted = module . exports . extractFunctionBody ( arg ) ;
483
+
484
+ runCalls . push (
485
+ ...module . exports . checkStatementsForTestInfo (
486
+ context ,
487
+ extracted ,
488
+ variableIdentifiers
489
+ )
490
+ ) ;
404
491
}
405
492
406
493
if (
407
- statement . type === 'ExpressionStatement' &&
408
- statement . expression . type === 'CallExpression' &&
409
- statement . expression . callee . type === 'MemberExpression' &&
410
- ( isRuleTesterConstruction ( statement . expression . callee . object ) ||
411
- variableIdentifiers . has ( statement . expression . callee . object ) ) &&
412
- statement . expression . callee . property . type === 'Identifier' &&
413
- statement . expression . callee . property . name === 'run'
494
+ expression . callee . type === 'MemberExpression' &&
495
+ ( isRuleTesterConstruction ( expression . callee . object ) ||
496
+ variableIdentifiers . has ( expression . callee . object ) ) &&
497
+ expression . callee . property . type === 'Identifier' &&
498
+ expression . callee . property . name === 'run'
414
499
) {
415
- runCalls . push ( statement . expression ) ;
500
+ runCalls . push ( expression ) ;
416
501
}
417
- } ) ;
502
+ }
503
+
504
+ return runCalls ;
505
+ } ,
506
+
507
+ /**
508
+ * Performs static analysis on an AST to try to find test cases
509
+ * @param {RuleContext } context The `context` variable for the source file itself
510
+ * @param {ASTNode } ast The `Program` node for the file.
511
+ * @returns {object } An object with `valid` and `invalid` keys containing a list of AST nodes corresponding to tests
512
+ */
513
+ getTestInfo ( context , ast ) {
514
+ const runCalls = module . exports . checkStatementsForTestInfo (
515
+ context ,
516
+ ast . body
517
+ ) ;
418
518
419
519
return runCalls
420
520
. filter (
0 commit comments