prefer-todo.js 1.74 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
'use strict';

const {
  getDocsUrl,
  isFunction,
  composeFixers,
  getNodeName,
  isString,
} = require('./util');

function isOnlyTestTitle(node) {
  return node.arguments.length === 1;
}

function isFunctionBodyEmpty(node) {
  return node.body.body && !node.body.body.length;
}

function isTestBodyEmpty(node) {
  const fn = node.arguments[1]; // eslint-disable-line prefer-destructuring
  return fn && isFunction(fn) && isFunctionBodyEmpty(fn);
}

function addTodo(node, fixer) {
  const testName = getNodeName(node.callee)
    .split('.')
    .shift();
  return fixer.replaceText(node.callee, `${testName}.todo`);
}

function removeSecondArg({ arguments: [first, second] }, fixer) {
  return fixer.removeRange([first.range[1], second.range[1]]);
}

function isFirstArgString({ arguments: [firstArg] }) {
  return firstArg && isString(firstArg);
}

const isTestCase = node =>
  node &&
  node.type === 'CallExpression' &&
  ['it', 'test', 'it.skip', 'test.skip'].includes(getNodeName(node.callee));

function create(context) {
  return {
    CallExpression(node) {
      if (isTestCase(node) && isFirstArgString(node)) {
        const combineFixers = composeFixers(node);

        if (isTestBodyEmpty(node)) {
          context.report({
            message: 'Prefer todo test case over empty test case',
            node,
            fix: combineFixers(removeSecondArg, addTodo),
          });
        }

        if (isOnlyTestTitle(node)) {
          context.report({
            message: 'Prefer todo test case over unimplemented test case',
            node,
            fix: combineFixers(addTodo),
          });
        }
      }
    },
  };
}

module.exports = {
  create,
  meta: {
    docs: {
      url: getDocsUrl(__filename),
    },
    fixable: 'code',
  },
};