Skip to content

⬅️ Back to Table of Contents

📄 no-dynamic-tests

📊 Analysis Summary

Metric Count
🔧 Functions 2
📦 Imports 4

📚 Table of Contents

🛠️ File Location:

📂 packages/eslint-plugin-internal/src/rules/no-dynamic-tests.ts

📤 Default Export

export default createRule({ ... })
Property Value
name 'no-dynamic-tests'
meta.type 'problem'
meta.docs.description 'Disallow dynamic syntax in RuleTester test arrays'
meta.fixable 'code'
meta.messages.noDynamicTests 'Dynamic syntax is not allowed in RuleTester test arrays. Use static values only.'
meta.messages.noFormatNotAllowedHere 'Dynamic syntax is not allowed in RuleTester test arrays. noFormat is only for the input code, not for other properti...
meta.schema []
defaultOptions []

Entry point: create — documented under Functions.


📦 Imports

Name Source
TSESTree @typescript-eslint/utils
AST_NODE_TYPES @typescript-eslint/utils
getRuleTesterCallObject ../util/getRuleTesterCallObject.js
createRule ../util/index.js

Functions

create(context: any): { CallExpression(node: any): void; }

Parameters:

  • context any

Returns: { CallExpression(node: any): void; }

Calls:

  • context.report
  • node.expressions.forEach
  • reportDynamicElements
  • node.elements.forEach
  • node.properties.forEach
  • keyToValidate.includes
  • fieldStack.at
  • fixer.remove
  • getRuleTesterCallObject (from ../util/getRuleTesterCallObject.js)
  • prop.value.elements.forEach

Internal Comments:

// InvalidTestCase extends ValidTestCase (x2)
// allow noFormat for the 'code' field only (or if a valid test case is using the string shorthand)

Code
create(context) {
    function reportDynamicElements(
      node: TSESTree.Node,
      fieldStack: string[],
    ): void {
      switch (node.type) {
        case AST_NODE_TYPES.Identifier:
          if (node.name === 'undefined') {
            break;
          }
          context.report({
            node,
            messageId: 'noDynamicTests',
          });
          break;
        case AST_NODE_TYPES.CallExpression:
        case AST_NODE_TYPES.SpreadElement:
        case AST_NODE_TYPES.BinaryExpression:
        case AST_NODE_TYPES.ConditionalExpression:
        case AST_NODE_TYPES.MemberExpression:
          context.report({
            node,
            messageId: 'noDynamicTests',
          });
          break;
        case AST_NODE_TYPES.TemplateLiteral:
          node.expressions.forEach(expr => {
            reportDynamicElements(expr, fieldStack);
          });
          break;
        case AST_NODE_TYPES.ArrayExpression:
          node.elements.forEach(element => {
            if (element) {
              reportDynamicElements(element, fieldStack);
            }
          });
          break;
        case AST_NODE_TYPES.ObjectExpression:
          node.properties.forEach(prop => {
            if (prop.type === AST_NODE_TYPES.SpreadElement) {
              context.report({
                node: prop,
                messageId: 'noDynamicTests',
              });
            } else {
              // InvalidTestCase extends ValidTestCase
              const keyToValidate = [
                'code',
                'errors',
                'output',
                'suggestions',
                'messageId',
              ];

              if (
                prop.key.type === AST_NODE_TYPES.Identifier &&
                keyToValidate.includes(prop.key.name)
              ) {
                reportDynamicElements(prop.value, [
                  ...fieldStack,
                  prop.key.name,
                ]);
              } else if (
                prop.key.type === AST_NODE_TYPES.Literal &&
                keyToValidate.includes(prop.key.value as string)
              ) {
                reportDynamicElements(prop.value, [
                  ...fieldStack,
                  prop.key.value as string,
                ]);
              }
            }
          });
          break;
        case AST_NODE_TYPES.TaggedTemplateExpression: {
          if (
            node.tag.type === AST_NODE_TYPES.Identifier &&
            node.tag.name === 'noFormat'
          ) {
            // allow noFormat for the 'code' field only (or if a valid test case is using the string shorthand)
            if (
              fieldStack.length === 1 ||
              (fieldStack.length === 2 && fieldStack.at(-1) === 'code')
            ) {
              break;
            }
            context.report({
              node: node.tag,
              messageId: 'noFormatNotAllowedHere',
              fix: fixer => fixer.remove(node.tag),
            });
            break;
          }

          context.report({
            node: node.tag,
            messageId: 'noDynamicTests',
          });
          break;
        }
        case AST_NODE_TYPES.Literal:
        default:
          break;
      }
    }

    return {
      CallExpression(node) {
        const testObject = getRuleTesterCallObject(node);
        if (!testObject) {
          return;
        }

        for (const prop of testObject.properties) {
          const isTestCases =
            prop.type === AST_NODE_TYPES.Property &&
            prop.key.type === AST_NODE_TYPES.Identifier &&
            (prop.key.name === 'valid' || prop.key.name === 'invalid');

          if (isTestCases) {
            const propName = (prop.key as TSESTree.Identifier).name;
            if (prop.value.type === AST_NODE_TYPES.ArrayExpression) {
              prop.value.elements.forEach(element => {
                if (element) {
                  reportDynamicElements(element, [propName]);
                }
              });
            } else {
              context.report({
                node: prop.value,
                messageId: 'noDynamicTests',
              });
            }
          }
        }
      },
    };
  }

Internal helpers

Declared inside another function in this file.

reportDynamicElements(node: TSESTree.Node, fieldStack: string[]): void

Parameters:

  • node TSESTree.Node
  • fieldStack string[]

Returns: void

Calls:

  • context.report
  • node.expressions.forEach
  • reportDynamicElements
  • node.elements.forEach
  • node.properties.forEach
  • keyToValidate.includes
  • fieldStack.at
  • fixer.remove

Internal Comments:

// InvalidTestCase extends ValidTestCase (x2)
// allow noFormat for the 'code' field only (or if a valid test case is using the string shorthand)

Code
function reportDynamicElements(
      node: TSESTree.Node,
      fieldStack: string[],
    ): void {
      switch (node.type) {
        case AST_NODE_TYPES.Identifier:
          if (node.name === 'undefined') {
            break;
          }
          context.report({
            node,
            messageId: 'noDynamicTests',
          });
          break;
        case AST_NODE_TYPES.CallExpression:
        case AST_NODE_TYPES.SpreadElement:
        case AST_NODE_TYPES.BinaryExpression:
        case AST_NODE_TYPES.ConditionalExpression:
        case AST_NODE_TYPES.MemberExpression:
          context.report({
            node,
            messageId: 'noDynamicTests',
          });
          break;
        case AST_NODE_TYPES.TemplateLiteral:
          node.expressions.forEach(expr => {
            reportDynamicElements(expr, fieldStack);
          });
          break;
        case AST_NODE_TYPES.ArrayExpression:
          node.elements.forEach(element => {
            if (element) {
              reportDynamicElements(element, fieldStack);
            }
          });
          break;
        case AST_NODE_TYPES.ObjectExpression:
          node.properties.forEach(prop => {
            if (prop.type === AST_NODE_TYPES.SpreadElement) {
              context.report({
                node: prop,
                messageId: 'noDynamicTests',
              });
            } else {
              // InvalidTestCase extends ValidTestCase
              const keyToValidate = [
                'code',
                'errors',
                'output',
                'suggestions',
                'messageId',
              ];

              if (
                prop.key.type === AST_NODE_TYPES.Identifier &&
                keyToValidate.includes(prop.key.name)
              ) {
                reportDynamicElements(prop.value, [
                  ...fieldStack,
                  prop.key.name,
                ]);
              } else if (
                prop.key.type === AST_NODE_TYPES.Literal &&
                keyToValidate.includes(prop.key.value as string)
              ) {
                reportDynamicElements(prop.value, [
                  ...fieldStack,
                  prop.key.value as string,
                ]);
              }
            }
          });
          break;
        case AST_NODE_TYPES.TaggedTemplateExpression: {
          if (
            node.tag.type === AST_NODE_TYPES.Identifier &&
            node.tag.name === 'noFormat'
          ) {
            // allow noFormat for the 'code' field only (or if a valid test case is using the string shorthand)
            if (
              fieldStack.length === 1 ||
              (fieldStack.length === 2 && fieldStack.at(-1) === 'code')
            ) {
              break;
            }
            context.report({
              node: node.tag,
              messageId: 'noFormatNotAllowedHere',
              fix: fixer => fixer.remove(node.tag),
            });
            break;
          }

          context.report({
            node: node.tag,
            messageId: 'noDynamicTests',
          });
          break;
        }
        case AST_NODE_TYPES.Literal:
        default:
          break;
      }
    }

Generated by Syntax Scribe