consistent-test-it.js 3.17 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
'use strict';

const { getDocsUrl, getNodeName, isTestCase, isDescribe } = require('./util');

module.exports = {
  meta: {
    docs: {
      url: getDocsUrl(__filename),
    },
    fixable: 'code',
    schema: [
      {
        type: 'object',
        properties: {
          fn: {
            enum: ['it', 'test'],
          },
          withinDescribe: {
            enum: ['it', 'test'],
          },
        },
        additionalProperties: false,
      },
    ],
  },
  create(context) {
    const configObj = context.options[0] || {};
    const testKeyword = configObj.fn || 'test';
    const testKeywordWithinDescribe =
      configObj.withinDescribe || configObj.fn || 'it';

    let describeNestingLevel = 0;

    return {
      CallExpression(node) {
        const nodeName = getNodeName(node.callee);

        if (isDescribe(node)) {
          describeNestingLevel++;
        }

        if (
          isTestCase(node) &&
          describeNestingLevel === 0 &&
          nodeName.indexOf(testKeyword) === -1
        ) {
          const oppositeTestKeyword = getOppositeTestKeyword(testKeyword);

          context.report({
            message:
              "Prefer using '{{ testKeyword }}' instead of '{{ oppositeTestKeyword }}'",
            node: node.callee,
            data: { testKeyword, oppositeTestKeyword },
            fix(fixer) {
              const nodeToReplace =
                node.callee.type === 'MemberExpression'
                  ? node.callee.object
                  : node.callee;

              const fixedNodeName = getPreferredNodeName(nodeName, testKeyword);
              return [fixer.replaceText(nodeToReplace, fixedNodeName)];
            },
          });
        }

        if (
          isTestCase(node) &&
          describeNestingLevel > 0 &&
          nodeName.indexOf(testKeywordWithinDescribe) === -1
        ) {
          const oppositeTestKeyword = getOppositeTestKeyword(
            testKeywordWithinDescribe
          );

          context.report({
            message:
              "Prefer using '{{ testKeywordWithinDescribe }}' instead of '{{ oppositeTestKeyword }}' within describe",
            node: node.callee,
            data: { testKeywordWithinDescribe, oppositeTestKeyword },
            fix(fixer) {
              const nodeToReplace =
                node.callee.type === 'MemberExpression'
                  ? node.callee.object
                  : node.callee;

              const fixedNodeName = getPreferredNodeName(
                nodeName,
                testKeywordWithinDescribe
              );
              return [fixer.replaceText(nodeToReplace, fixedNodeName)];
            },
          });
        }
      },
      'CallExpression:exit'(node) {
        if (isDescribe(node)) {
          describeNestingLevel--;
        }
      },
    };
  },
};

function getPreferredNodeName(nodeName, preferredTestKeyword) {
  switch (nodeName) {
    case 'fit':
      return 'test.only';
    default:
      return nodeName.startsWith('f') || nodeName.startsWith('x')
        ? nodeName.charAt(0) + preferredTestKeyword
        : preferredTestKeyword;
  }
}

function getOppositeTestKeyword(test) {
  if (test === 'test') {
    return 'it';
  }

  return 'test';
}