'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
exports.default = void 0;

var _jestMatcherUtils = require('jest-matcher-utils');

var _jasmineUtils = require('./jasmineUtils');

var _utils = require('./utils');

var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;

function _slicedToArray(arr, i) {
  return (
    _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest()
  );
}

function _nonIterableRest() {
  throw new TypeError('Invalid attempt to destructure non-iterable instance');
}

function _iterableToArrayLimit(arr, i) {
  var _arr = [];
  var _n = true;
  var _d = false;
  var _e = undefined;
  try {
    for (
      var _i = arr[Symbol.iterator](), _s;
      !(_n = (_s = _i.next()).done);
      _n = true
    ) {
      _arr.push(_s.value);
      if (i && _arr.length === i) break;
    }
  } catch (err) {
    _d = true;
    _e = err;
  } finally {
    try {
      if (!_n && _i['return'] != null) _i['return']();
    } finally {
      if (_d) throw _e;
    }
  }
  return _arr;
}

function _arrayWithHoles(arr) {
  if (Array.isArray(arr)) return arr;
}

const CALL_PRINT_LIMIT = 3;
const RETURN_PRINT_LIMIT = 5;
const LAST_CALL_PRINT_LIMIT = 1;

const createToBeCalledMatcher = matcherName => (received, expected) => {
  (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName);
  ensureMock(received, matcherName);
  const receivedIsSpy = isSpy(received);
  const type = receivedIsSpy ? 'spy' : 'mock function';
  const receivedName = receivedIsSpy ? 'spy' : received.getMockName();
  const identifier =
    receivedIsSpy || receivedName === 'jest.fn()'
      ? type
      : `${type} "${receivedName}"`;
  const count = receivedIsSpy
    ? received.calls.count()
    : received.mock.calls.length;
  const calls = receivedIsSpy
    ? received.calls.all().map(x => x.args)
    : received.mock.calls;
  const pass = count > 0;
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)(
          '.not' + matcherName,
          receivedName,
          ''
        ) +
        '\n\n' +
        `Expected ${identifier} not to be called ` +
        formatReceivedCalls(calls, CALL_PRINT_LIMIT, {
          sameSentence: true
        })
    : () =>
        (0, _jestMatcherUtils.matcherHint)(matcherName, receivedName, '') +
        '\n\n' +
        `Expected ${identifier} to have been called, but it was not called.`;
  return {
    message,
    pass
  };
};

const createToReturnMatcher = matcherName => (received, expected) => {
  (0, _jestMatcherUtils.ensureNoExpected)(expected, matcherName);
  ensureMock(received, matcherName);
  const receivedName = received.getMockName();
  const identifier =
    receivedName === 'jest.fn()'
      ? 'mock function'
      : `mock function "${receivedName}"`; // List of return values that correspond only to calls that returned

  const returnValues = received.mock.results
    .filter(result => result.type === 'return')
    .map(result => result.value);
  const count = returnValues.length;
  const pass = count > 0;
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)(
          '.not' + matcherName,
          receivedName,
          ''
        ) +
        '\n\n' +
        `Expected ${identifier} not to have returned, but it returned:\n` +
        `  ${getPrintedReturnValues(returnValues, RETURN_PRINT_LIMIT)}`
    : () =>
        (0, _jestMatcherUtils.matcherHint)(matcherName, receivedName, '') +
        '\n\n' +
        `Expected ${identifier} to have returned.`;
  return {
    message,
    pass
  };
};

const createToBeCalledTimesMatcher = matcherName => (received, expected) => {
  (0, _jestMatcherUtils.ensureExpectedIsNumber)(expected, matcherName);
  ensureMock(received, matcherName);
  const receivedIsSpy = isSpy(received);
  const type = receivedIsSpy ? 'spy' : 'mock function';
  const receivedName = receivedIsSpy ? 'spy' : received.getMockName();
  const identifier =
    receivedIsSpy || receivedName === 'jest.fn()'
      ? type
      : `${type} "${receivedName}"`;
  const count = receivedIsSpy
    ? received.calls.count()
    : received.mock.calls.length;
  const pass = count === expected;
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)(
          '.not' + matcherName,
          receivedName,
          String(expected)
        ) +
        `\n\n` +
        `Expected ${identifier} not to be called ` +
        `${(0, _jestMatcherUtils.EXPECTED_COLOR)(
          (0, _jestMatcherUtils.pluralize)('time', expected)
        )}, but it was` +
        ` called exactly ${(0, _jestMatcherUtils.RECEIVED_COLOR)(
          (0, _jestMatcherUtils.pluralize)('time', count)
        )}.`
    : () =>
        (0, _jestMatcherUtils.matcherHint)(
          matcherName,
          receivedName,
          String(expected)
        ) +
        '\n\n' +
        `Expected ${identifier} to have been called ` +
        `${(0, _jestMatcherUtils.EXPECTED_COLOR)(
          (0, _jestMatcherUtils.pluralize)('time', expected)
        )},` +
        ` but it was called ${(0, _jestMatcherUtils.RECEIVED_COLOR)(
          (0, _jestMatcherUtils.pluralize)('time', count)
        )}.`;
  return {
    message,
    pass
  };
};

const createToReturnTimesMatcher = matcherName => (received, expected) => {
  (0, _jestMatcherUtils.ensureExpectedIsNumber)(expected, matcherName);
  ensureMock(received, matcherName);
  const receivedName = received.getMockName();
  const identifier =
    receivedName === 'jest.fn()'
      ? 'mock function'
      : `mock function "${receivedName}"`; // List of return results that correspond only to calls that returned

  const returnResults = received.mock.results.filter(
    result => result.type === 'return'
  );
  const count = returnResults.length;
  const pass = count === expected;
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)(
          '.not' + matcherName,
          receivedName,
          String(expected)
        ) +
        `\n\n` +
        `Expected ${identifier} not to have returned ` +
        `${(0, _jestMatcherUtils.EXPECTED_COLOR)(
          (0, _jestMatcherUtils.pluralize)('time', expected)
        )}, but it` +
        ` returned exactly ${(0, _jestMatcherUtils.RECEIVED_COLOR)(
          (0, _jestMatcherUtils.pluralize)('time', count)
        )}.`
    : () =>
        (0, _jestMatcherUtils.matcherHint)(
          matcherName,
          receivedName,
          String(expected)
        ) +
        '\n\n' +
        `Expected ${identifier} to have returned ` +
        `${(0, _jestMatcherUtils.EXPECTED_COLOR)(
          (0, _jestMatcherUtils.pluralize)('time', expected)
        )},` +
        ` but it returned ${(0, _jestMatcherUtils.RECEIVED_COLOR)(
          (0, _jestMatcherUtils.pluralize)('time', count)
        )}.`;
  return {
    message,
    pass
  };
};

const createToBeCalledWithMatcher = matcherName => (received, ...expected) => {
  ensureMock(received, matcherName);
  const receivedIsSpy = isSpy(received);
  const type = receivedIsSpy ? 'spy' : 'mock function';
  const receivedName = receivedIsSpy ? 'spy' : received.getMockName();
  const identifier =
    receivedIsSpy || receivedName === 'jest.fn()'
      ? type
      : `${type} "${receivedName}"`;
  const calls = receivedIsSpy
    ? received.calls.all().map(x => x.args)
    : received.mock.calls;

  const _partition = (0, _utils.partition)(calls, call =>
      (0, _jasmineUtils.equals)(call, expected, [_utils.iterableEquality])
    ),
    _partition2 = _slicedToArray(_partition, 2),
    match = _partition2[0],
    fail = _partition2[1];

  const pass = match.length > 0;
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)('.not' + matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} not to have been called with:\n` +
        `  ${(0, _jestMatcherUtils.printExpected)(expected)}`
    : () =>
        (0, _jestMatcherUtils.matcherHint)(matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} to have been called with:\n` +
        formatMismatchedCalls(fail, expected, CALL_PRINT_LIMIT);
  return {
    message,
    pass
  };
};

const createToReturnWithMatcher = matcherName => (received, expected) => {
  ensureMock(received, matcherName);
  const receivedName = received.getMockName();
  const identifier =
    receivedName === 'jest.fn()'
      ? 'mock function'
      : `mock function "${receivedName}"`; // List of return values that correspond only to calls that returned

  const returnValues = received.mock.results
    .filter(result => result.type === 'return')
    .map(result => result.value);

  const _partition3 = (0, _utils.partition)(returnValues, value =>
      (0, _jasmineUtils.equals)(expected, value, [_utils.iterableEquality])
    ),
    _partition4 = _slicedToArray(_partition3, 1),
    match = _partition4[0];

  const pass = match.length > 0;
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)('.not' + matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} not to have returned:\n` +
        `  ${(0, _jestMatcherUtils.printExpected)(expected)}\n` +
        `But it returned exactly:\n` +
        `  ${(0, _jestMatcherUtils.printReceived)(expected)}`
    : () =>
        (0, _jestMatcherUtils.matcherHint)(matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} to have returned:\n` +
        formatMismatchedReturnValues(
          returnValues,
          expected,
          RETURN_PRINT_LIMIT
        );
  return {
    message,
    pass
  };
};

const createLastCalledWithMatcher = matcherName => (received, ...expected) => {
  ensureMock(received, matcherName);
  const receivedIsSpy = isSpy(received);
  const type = receivedIsSpy ? 'spy' : 'mock function';
  const receivedName = receivedIsSpy ? 'spy' : received.getMockName();
  const identifier =
    receivedIsSpy || receivedName === 'jest.fn()'
      ? type
      : `${type} "${receivedName}"`;
  const calls = receivedIsSpy
    ? received.calls.all().map(x => x.args)
    : received.mock.calls;
  const pass = (0, _jasmineUtils.equals)(calls[calls.length - 1], expected, [
    _utils.iterableEquality
  ]);
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)('.not' + matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} to not have been last called with:\n` +
        `  ${(0, _jestMatcherUtils.printExpected)(expected)}`
    : () =>
        (0, _jestMatcherUtils.matcherHint)(matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} to have been last called with:\n` +
        formatMismatchedCalls(calls, expected, LAST_CALL_PRINT_LIMIT);
  return {
    message,
    pass
  };
};

const createLastReturnedMatcher = matcherName => (received, expected) => {
  ensureMock(received, matcherName);
  const receivedName = received.getMockName();
  const identifier =
    receivedName === 'jest.fn()'
      ? 'mock function'
      : `mock function "${receivedName}"`;
  const results = received.mock.results;
  const lastResult = results[results.length - 1];
  const pass =
    !!lastResult &&
    lastResult.type === 'return' &&
    (0, _jasmineUtils.equals)(lastResult.value, expected, [
      _utils.iterableEquality
    ]);
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)('.not' + matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} to not have last returned:\n` +
        `  ${(0, _jestMatcherUtils.printExpected)(expected)}\n` +
        `But it last returned exactly:\n` +
        `  ${(0, _jestMatcherUtils.printReceived)(lastResult.value)}`
    : () =>
        (0, _jestMatcherUtils.matcherHint)(matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} to have last returned:\n` +
        `  ${(0, _jestMatcherUtils.printExpected)(expected)}\n` +
        (!lastResult
          ? `But it was ${(0, _jestMatcherUtils.RECEIVED_COLOR)('not called')}`
          : lastResult.type === 'incomplete'
          ? `But the last call ${(0, _jestMatcherUtils.RECEIVED_COLOR)(
              'has not returned yet'
            )}`
          : lastResult.type === 'throw'
          ? `But the last call ${(0, _jestMatcherUtils.RECEIVED_COLOR)(
              'threw an error'
            )}`
          : `But the last call returned:\n  ${(0,
            _jestMatcherUtils.printReceived)(lastResult.value)}`);
  return {
    message,
    pass
  };
};

const createNthCalledWithMatcher = matcherName => (
  received,
  nth,
  ...expected
) => {
  ensureMock(received, matcherName);
  const receivedIsSpy = isSpy(received);
  const type = receivedIsSpy ? 'spy' : 'mock function'; // @ts-ignore

  if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) {
    const message = () =>
      `nth value ${(0, _jestMatcherUtils.printReceived)(
        nth
      )} must be a positive integer greater than ${(0,
      _jestMatcherUtils.printExpected)(0)}`;

    const pass = false;
    return {
      message,
      pass
    };
  }

  const receivedName = receivedIsSpy ? 'spy' : received.getMockName();
  const identifier =
    receivedIsSpy || receivedName === 'jest.fn()'
      ? type
      : `${type} "${receivedName}"`;
  const calls = receivedIsSpy
    ? received.calls.all().map(x => x.args)
    : received.mock.calls;
  const pass = (0, _jasmineUtils.equals)(calls[nth - 1], expected, [
    _utils.iterableEquality
  ]);
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)('.not' + matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} ${nthToString(
          nth
        )} call to not have been called with:\n` +
        `  ${(0, _jestMatcherUtils.printExpected)(expected)}`
    : () =>
        (0, _jestMatcherUtils.matcherHint)(matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} ${nthToString(
          nth
        )} call to have been called with:\n` +
        formatMismatchedCalls(
          calls[nth - 1] ? [calls[nth - 1]] : [],
          expected,
          LAST_CALL_PRINT_LIMIT
        );
  return {
    message,
    pass
  };
};

const createNthReturnedWithMatcher = matcherName => (
  received,
  nth,
  expected
) => {
  ensureMock(received, matcherName); //@ts-ignore

  if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) {
    const message = () =>
      `nth value ${(0, _jestMatcherUtils.printReceived)(
        nth
      )} must be a positive integer greater than ${(0,
      _jestMatcherUtils.printExpected)(0)}`;

    const pass = false;
    return {
      message,
      pass
    };
  }

  const receivedName = received.getMockName();
  const identifier =
    receivedName === 'jest.fn()'
      ? 'mock function'
      : `mock function "${receivedName}"`;
  const results = received.mock.results;
  const nthResult = results[nth - 1];
  const pass =
    !!nthResult &&
    nthResult.type === 'return' &&
    (0, _jasmineUtils.equals)(nthResult.value, expected, [
      _utils.iterableEquality
    ]);
  const nthString = nthToString(nth);
  const message = pass
    ? () =>
        (0, _jestMatcherUtils.matcherHint)('.not' + matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} ${nthString} call to not have returned with:\n` +
        `  ${(0, _jestMatcherUtils.printExpected)(expected)}\n` +
        `But the ${nthString} call returned exactly:\n` +
        `  ${(0, _jestMatcherUtils.printReceived)(nthResult.value)}`
    : () =>
        (0, _jestMatcherUtils.matcherHint)(matcherName, receivedName) +
        '\n\n' +
        `Expected ${identifier} ${nthString} call to have returned with:\n` +
        `  ${(0, _jestMatcherUtils.printExpected)(expected)}\n` +
        (results.length === 0
          ? `But it was ${(0, _jestMatcherUtils.RECEIVED_COLOR)('not called')}`
          : nth > results.length
          ? `But it was only called ${(0, _jestMatcherUtils.printReceived)(
              results.length
            )} times`
          : nthResult.type === 'incomplete'
          ? `But the ${nthString} call ${(0, _jestMatcherUtils.RECEIVED_COLOR)(
              'has not returned yet'
            )}`
          : nthResult.type === 'throw'
          ? `But the ${nthString} call ${(0, _jestMatcherUtils.RECEIVED_COLOR)(
              'threw an error'
            )}`
          : `But the ${nthString} call returned with:\n  ${(0,
            _jestMatcherUtils.printReceived)(nthResult.value)}`);
  return {
    message,
    pass
  };
};

const spyMatchers = {
  lastCalledWith: createLastCalledWithMatcher('.lastCalledWith'),
  lastReturnedWith: createLastReturnedMatcher('.lastReturnedWith'),
  nthCalledWith: createNthCalledWithMatcher('.nthCalledWith'),
  nthReturnedWith: createNthReturnedWithMatcher('.nthReturnedWith'),
  toBeCalled: createToBeCalledMatcher('.toBeCalled'),
  toBeCalledTimes: createToBeCalledTimesMatcher('.toBeCalledTimes'),
  toBeCalledWith: createToBeCalledWithMatcher('.toBeCalledWith'),
  toHaveBeenCalled: createToBeCalledMatcher('.toHaveBeenCalled'),
  toHaveBeenCalledTimes: createToBeCalledTimesMatcher('.toHaveBeenCalledTimes'),
  toHaveBeenCalledWith: createToBeCalledWithMatcher('.toHaveBeenCalledWith'),
  toHaveBeenLastCalledWith: createLastCalledWithMatcher(
    '.toHaveBeenLastCalledWith'
  ),
  toHaveBeenNthCalledWith: createNthCalledWithMatcher(
    '.toHaveBeenNthCalledWith'
  ),
  toHaveLastReturnedWith: createLastReturnedMatcher('.toHaveLastReturnedWith'),
  toHaveNthReturnedWith: createNthReturnedWithMatcher('.toHaveNthReturnedWith'),
  toHaveReturned: createToReturnMatcher('.toHaveReturned'),
  toHaveReturnedTimes: createToReturnTimesMatcher('.toHaveReturnedTimes'),
  toHaveReturnedWith: createToReturnWithMatcher('.toHaveReturnedWith'),
  toReturn: createToReturnMatcher('.toReturn'),
  toReturnTimes: createToReturnTimesMatcher('.toReturnTimes'),
  toReturnWith: createToReturnWithMatcher('.toReturnWith')
};

const isSpy = spy => spy.calls && typeof spy.calls.count === 'function';

const ensureMock = (mockOrSpy, matcherName) => {
  if (
    !mockOrSpy ||
    ((mockOrSpy.calls === undefined || mockOrSpy.calls.all === undefined) &&
      mockOrSpy._isMockFunction !== true)
  ) {
    throw new Error(
      (0, _jestMatcherUtils.matcherErrorMessage)(
        (0, _jestMatcherUtils.matcherHint)(
          '[.not]' + matcherName,
          'jest.fn()',
          ''
        ),
        `${(0, _jestMatcherUtils.RECEIVED_COLOR)(
          'received'
        )} value must be a mock or spy function`,
        (0, _jestMatcherUtils.printWithType)(
          'Received',
          mockOrSpy,
          _jestMatcherUtils.printReceived
        )
      )
    );
  }
};

const getPrintedCalls = (calls, limit, sep, fn) => {
  const result = [];
  let i = calls.length;

  while (--i >= 0 && --limit >= 0) {
    result.push(fn(calls[i]));
  }

  return result.join(sep);
};

const getPrintedReturnValues = (calls, limit) => {
  const result = [];

  for (let i = 0; i < calls.length && i < limit; i += 1) {
    result.push((0, _jestMatcherUtils.printReceived)(calls[i]));
  }

  if (calls.length > limit) {
    result.push(
      `...and ${(0, _jestMatcherUtils.printReceived)(
        calls.length - limit
      )} more`
    );
  }

  return result.join('\n\n  ');
};

const formatReceivedCalls = (calls, limit, options) => {
  if (calls.length) {
    const but = options && options.sameSentence ? 'but' : 'But';
    const count = calls.length - limit;
    const printedCalls = getPrintedCalls(
      calls,
      limit,
      ', ',
      _jestMatcherUtils.printReceived
    );
    return (
      `${but} it was called ` +
      `with:\n  ` +
      printedCalls +
      (count > 0
        ? '\nand ' +
          (0, _jestMatcherUtils.RECEIVED_COLOR)(
            (0, _jestMatcherUtils.pluralize)('more call', count)
          ) +
          '.'
        : '')
    );
  } else {
    return `But it was ${(0, _jestMatcherUtils.RECEIVED_COLOR)('not called')}.`;
  }
};

const formatMismatchedCalls = (calls, expected, limit) => {
  if (calls.length) {
    return getPrintedCalls(
      calls,
      limit,
      '\n\n',
      formatMismatchedArgs.bind(null, expected)
    );
  } else {
    return (
      `  ${(0, _jestMatcherUtils.printExpected)(expected)}\n` +
      `But it was ${(0, _jestMatcherUtils.RECEIVED_COLOR)('not called')}.`
    );
  }
};

const formatMismatchedReturnValues = (returnValues, expected, limit) => {
  if (returnValues.length) {
    return (
      `  ${(0, _jestMatcherUtils.printExpected)(expected)}\n` +
      `But it returned:\n` +
      `  ${getPrintedReturnValues(returnValues, limit)}`
    );
  } else {
    return (
      `  ${(0, _jestMatcherUtils.printExpected)(expected)}\n` +
      `But it did ${(0, _jestMatcherUtils.RECEIVED_COLOR)('not return')}.`
    );
  }
};

const formatMismatchedArgs = (expected, received) => {
  const length = Math.max(expected.length, received.length);
  const printedArgs = [];

  for (let i = 0; i < length; i++) {
    if (
      !(0, _jasmineUtils.equals)(expected[i], received[i], [
        _utils.iterableEquality
      ])
    ) {
      const oneline = (0, _utils.isOneline)(expected[i], received[i]);
      const diffString = (0, _jestMatcherUtils.diff)(expected[i], received[i]);
      printedArgs.push(
        `  ${(0, _jestMatcherUtils.printExpected)(expected[i])}\n` +
          `as argument ${i + 1}, but it was called with\n` +
          `  ${(0, _jestMatcherUtils.printReceived)(received[i])}.` +
          (diffString && !oneline ? `\n\nDifference:\n\n${diffString}` : '')
      );
    } else if (i >= expected.length) {
      printedArgs.push(
        `  Did not expect argument ${i + 1} ` +
          `but it was called with ${(0, _jestMatcherUtils.printReceived)(
            received[i]
          )}.`
      );
    }
  }

  return printedArgs.join('\n');
};

const nthToString = nth => {
  switch (nth) {
    case 1:
      return 'first';

    case 2:
      return 'second';

    case 3:
      return 'third';
  }

  return `${nth}th`;
};

var _default = spyMatchers;
exports.default = _default;
