prefer-spy-on.js 1.76 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
'use strict';

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

const getJestFnCall = node => {
  if (
    (node.type !== 'CallExpression' && node.type !== 'MemberExpression') ||
    (node.callee && node.callee.type !== 'MemberExpression')
  ) {
    return null;
  }

  const obj = node.callee ? node.callee.object : node.object;

  if (obj.type === 'Identifier') {
    return node.type === 'CallExpression' &&
      getNodeName(node.callee) === 'jest.fn'
      ? node
      : null;
  }

  return getJestFnCall(obj);
};

module.exports = {
  meta: {
    docs: {
      url: getDocsUrl(__filename),
    },
    fixable: 'code',
  },
  create(context) {
    return {
      AssignmentExpression(node) {
        if (node.left.type !== 'MemberExpression') return;

        const jestFnCall = getJestFnCall(node.right);

        if (!jestFnCall) return;

        context.report({
          node,
          message: 'Use jest.spyOn() instead.',
          fix(fixer) {
            const leftPropQuote =
              node.left.property.type === 'Identifier' ? "'" : '';
            const [arg] = jestFnCall.arguments;
            const argSource = arg && context.getSourceCode().getText(arg);
            const mockImplementation = argSource
              ? `.mockImplementation(${argSource})`
              : '';

            return [
              fixer.insertTextBefore(node.left, `jest.spyOn(`),
              fixer.replaceTextRange(
                [node.left.object.range[1], node.left.property.range[0]],
                `, ${leftPropQuote}`
              ),
              fixer.replaceTextRange(
                [node.left.property.range[1], jestFnCall.range[1]],
                `${leftPropQuote})${mockImplementation}`
              ),
            ];
          },
        });
      },
    };
  },
};