'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _has = require('has');

var _has2 = _interopRequireDefault(_has);

var _tmatch = require('tmatch');

var _tmatch2 = _interopRequireDefault(_tmatch);

var _assert = require('./assert');

var _assert2 = _interopRequireDefault(_assert);

var _SpyUtils = require('./SpyUtils');

var _TestUtils = require('./TestUtils');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/**
 * An Expectation is a wrapper around an assertion that allows it to be written
 * in a more natural style, without the need to remember the order of arguments.
 * This helps prevent you from making mistakes when writing tests.
 */

var Expectation = function () {
  function Expectation(actual) {
    _classCallCheck(this, Expectation);

    this.actual = actual;

    if ((0, _TestUtils.isFunction)(actual)) {
      this.context = null;
      this.args = [];
    }
  }

  _createClass(Expectation, [{
    key: 'toExist',
    value: function toExist(message) {
      (0, _assert2.default)(this.actual, message || 'Expected %s to exist', this.actual);

      return this;
    }
  }, {
    key: 'toNotExist',
    value: function toNotExist(message) {
      (0, _assert2.default)(!this.actual, message || 'Expected %s to not exist', this.actual);

      return this;
    }
  }, {
    key: 'toBe',
    value: function toBe(value, message) {
      (0, _assert2.default)(this.actual === value, message || 'Expected %s to be %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toNotBe',
    value: function toNotBe(value, message) {
      (0, _assert2.default)(this.actual !== value, message || 'Expected %s to not be %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toEqual',
    value: function toEqual(value, message) {
      try {
        (0, _assert2.default)((0, _TestUtils.isEqual)(this.actual, value), message || 'Expected %s to equal %s', this.actual, value);
      } catch (error) {
        // These attributes are consumed by Mocha to produce a diff output.
        error.actual = this.actual;
        error.expected = value;
        error.showDiff = true;
        throw error;
      }

      return this;
    }
  }, {
    key: 'toNotEqual',
    value: function toNotEqual(value, message) {
      (0, _assert2.default)(!(0, _TestUtils.isEqual)(this.actual, value), message || 'Expected %s to not equal %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toThrow',
    value: function toThrow(value, message) {
      (0, _assert2.default)((0, _TestUtils.isFunction)(this.actual), 'The "actual" argument in expect(actual).toThrow() must be a function, %s was given', this.actual);

      (0, _assert2.default)((0, _TestUtils.functionThrows)(this.actual, this.context, this.args, value), message || 'Expected %s to throw %s', this.actual, value || 'an error');

      return this;
    }
  }, {
    key: 'toNotThrow',
    value: function toNotThrow(value, message) {
      (0, _assert2.default)((0, _TestUtils.isFunction)(this.actual), 'The "actual" argument in expect(actual).toNotThrow() must be a function, %s was given', this.actual);

      (0, _assert2.default)(!(0, _TestUtils.functionThrows)(this.actual, this.context, this.args, value), message || 'Expected %s to not throw %s', this.actual, value || 'an error');

      return this;
    }
  }, {
    key: 'toBeA',
    value: function toBeA(value, message) {
      (0, _assert2.default)((0, _TestUtils.isFunction)(value) || typeof value === 'string', 'The "value" argument in toBeA(value) must be a function or a string');

      (0, _assert2.default)((0, _TestUtils.isA)(this.actual, value), message || 'Expected %s to be a %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toNotBeA',
    value: function toNotBeA(value, message) {
      (0, _assert2.default)((0, _TestUtils.isFunction)(value) || typeof value === 'string', 'The "value" argument in toNotBeA(value) must be a function or a string');

      (0, _assert2.default)(!(0, _TestUtils.isA)(this.actual, value), message || 'Expected %s to not be a %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toMatch',
    value: function toMatch(pattern, message) {
      (0, _assert2.default)((0, _tmatch2.default)(this.actual, pattern), message || 'Expected %s to match %s', this.actual, pattern);

      return this;
    }
  }, {
    key: 'toNotMatch',
    value: function toNotMatch(pattern, message) {
      (0, _assert2.default)(!(0, _tmatch2.default)(this.actual, pattern), message || 'Expected %s to not match %s', this.actual, pattern);

      return this;
    }
  }, {
    key: 'toBeLessThan',
    value: function toBeLessThan(value, message) {
      (0, _assert2.default)(typeof this.actual === 'number', 'The "actual" argument in expect(actual).toBeLessThan() must be a number');

      (0, _assert2.default)(typeof value === 'number', 'The "value" argument in toBeLessThan(value) must be a number');

      (0, _assert2.default)(this.actual < value, message || 'Expected %s to be less than %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toBeLessThanOrEqualTo',
    value: function toBeLessThanOrEqualTo(value, message) {
      (0, _assert2.default)(typeof this.actual === 'number', 'The "actual" argument in expect(actual).toBeLessThanOrEqualTo() must be a number');

      (0, _assert2.default)(typeof value === 'number', 'The "value" argument in toBeLessThanOrEqualTo(value) must be a number');

      (0, _assert2.default)(this.actual <= value, message || 'Expected %s to be less than or equal to %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toBeGreaterThan',
    value: function toBeGreaterThan(value, message) {
      (0, _assert2.default)(typeof this.actual === 'number', 'The "actual" argument in expect(actual).toBeGreaterThan() must be a number');

      (0, _assert2.default)(typeof value === 'number', 'The "value" argument in toBeGreaterThan(value) must be a number');

      (0, _assert2.default)(this.actual > value, message || 'Expected %s to be greater than %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toBeGreaterThanOrEqualTo',
    value: function toBeGreaterThanOrEqualTo(value, message) {
      (0, _assert2.default)(typeof this.actual === 'number', 'The "actual" argument in expect(actual).toBeGreaterThanOrEqualTo() must be a number');

      (0, _assert2.default)(typeof value === 'number', 'The "value" argument in toBeGreaterThanOrEqualTo(value) must be a number');

      (0, _assert2.default)(this.actual >= value, message || 'Expected %s to be greater than or equal to %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toInclude',
    value: function toInclude(value, compareValues, message) {
      if (typeof compareValues === 'string') {
        message = compareValues;
        compareValues = null;
      }

      if (compareValues == null) compareValues = _TestUtils.isEqual;

      var contains = false;

      if ((0, _TestUtils.isArray)(this.actual)) {
        contains = (0, _TestUtils.arrayContains)(this.actual, value, compareValues);
      } else if ((0, _TestUtils.isObject)(this.actual)) {
        contains = (0, _TestUtils.objectContains)(this.actual, value, compareValues);
      } else if (typeof this.actual === 'string') {
        contains = (0, _TestUtils.stringContains)(this.actual, value);
      } else {
        (0, _assert2.default)(false, 'The "actual" argument in expect(actual).toInclude() must be an array, object, or a string');
      }

      (0, _assert2.default)(contains, message || 'Expected %s to include %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toExclude',
    value: function toExclude(value, compareValues, message) {
      if (typeof compareValues === 'string') {
        message = compareValues;
        compareValues = null;
      }

      if (compareValues == null) compareValues = _TestUtils.isEqual;

      var contains = false;

      if ((0, _TestUtils.isArray)(this.actual)) {
        contains = (0, _TestUtils.arrayContains)(this.actual, value, compareValues);
      } else if ((0, _TestUtils.isObject)(this.actual)) {
        contains = (0, _TestUtils.objectContains)(this.actual, value, compareValues);
      } else if (typeof this.actual === 'string') {
        contains = (0, _TestUtils.stringContains)(this.actual, value);
      } else {
        (0, _assert2.default)(false, 'The "actual" argument in expect(actual).toExclude() must be an array, object, or a string');
      }

      (0, _assert2.default)(!contains, message || 'Expected %s to exclude %s', this.actual, value);

      return this;
    }
  }, {
    key: 'toIncludeKeys',
    value: function toIncludeKeys(keys, comparator, message) {
      var _this = this;

      if (typeof comparator === 'string') {
        message = comparator;
        comparator = null;
      }

      if (comparator == null) comparator = _has2.default;

      (0, _assert2.default)(_typeof(this.actual) === 'object', 'The "actual" argument in expect(actual).toIncludeKeys() must be an object, not %s', this.actual);

      (0, _assert2.default)((0, _TestUtils.isArray)(keys), 'The "keys" argument in expect(actual).toIncludeKeys(keys) must be an array, not %s', keys);

      var contains = keys.every(function (key) {
        return comparator(_this.actual, key);
      });

      (0, _assert2.default)(contains, message || 'Expected %s to include key(s) %s', this.actual, keys.join(', '));

      return this;
    }
  }, {
    key: 'toIncludeKey',
    value: function toIncludeKey(key) {
      for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
        args[_key - 1] = arguments[_key];
      }

      return this.toIncludeKeys.apply(this, [[key]].concat(args));
    }
  }, {
    key: 'toExcludeKeys',
    value: function toExcludeKeys(keys, comparator, message) {
      var _this2 = this;

      if (typeof comparator === 'string') {
        message = comparator;
        comparator = null;
      }

      if (comparator == null) comparator = _has2.default;

      (0, _assert2.default)(_typeof(this.actual) === 'object', 'The "actual" argument in expect(actual).toExcludeKeys() must be an object, not %s', this.actual);

      (0, _assert2.default)((0, _TestUtils.isArray)(keys), 'The "keys" argument in expect(actual).toIncludeKeys(keys) must be an array, not %s', keys);

      var contains = keys.every(function (key) {
        return comparator(_this2.actual, key);
      });

      (0, _assert2.default)(!contains, message || 'Expected %s to exclude key(s) %s', this.actual, keys.join(', '));

      return this;
    }
  }, {
    key: 'toExcludeKey',
    value: function toExcludeKey(key) {
      for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
        args[_key2 - 1] = arguments[_key2];
      }

      return this.toExcludeKeys.apply(this, [[key]].concat(args));
    }
  }, {
    key: 'toHaveBeenCalled',
    value: function toHaveBeenCalled(message) {
      var spy = this.actual;

      (0, _assert2.default)((0, _SpyUtils.isSpy)(spy), 'The "actual" argument in expect(actual).toHaveBeenCalled() must be a spy');

      (0, _assert2.default)(spy.calls.length > 0, message || 'spy was not called');

      return this;
    }
  }, {
    key: 'toHaveBeenCalledWith',
    value: function toHaveBeenCalledWith() {
      for (var _len3 = arguments.length, expectedArgs = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
        expectedArgs[_key3] = arguments[_key3];
      }

      var spy = this.actual;

      (0, _assert2.default)((0, _SpyUtils.isSpy)(spy), 'The "actual" argument in expect(actual).toHaveBeenCalledWith() must be a spy');

      (0, _assert2.default)(spy.calls.some(function (call) {
        return (0, _TestUtils.isEqual)(call.arguments, expectedArgs);
      }), 'spy was never called with %s', expectedArgs);

      return this;
    }
  }, {
    key: 'toNotHaveBeenCalled',
    value: function toNotHaveBeenCalled(message) {
      var spy = this.actual;

      (0, _assert2.default)((0, _SpyUtils.isSpy)(spy), 'The "actual" argument in expect(actual).toNotHaveBeenCalled() must be a spy');

      (0, _assert2.default)(spy.calls.length === 0, message || 'spy was not supposed to be called');

      return this;
    }
  }]);

  return Expectation;
}();

var deprecate = function deprecate(fn, message) {
  var alreadyWarned = false;

  return function () {
    if (!alreadyWarned) {
      alreadyWarned = true;
      console.warn(message);
    }

    for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
      args[_key4] = arguments[_key4];
    }

    return fn.apply(this, args);
  };
};

Expectation.prototype.withContext = deprecate(function (context) {
  (0, _assert2.default)((0, _TestUtils.isFunction)(this.actual), 'The "actual" argument in expect(actual).withContext() must be a function');

  this.context = context;

  return this;
}, '\nwithContext is deprecated; use a closure instead.\n\n  expect(fn).withContext(context).toThrow()\n\nbecomes\n\n  expect(() => fn.call(context)).toThrow()\n');

Expectation.prototype.withArgs = deprecate(function () {
  var _args;

  (0, _assert2.default)((0, _TestUtils.isFunction)(this.actual), 'The "actual" argument in expect(actual).withArgs() must be a function');

  if (arguments.length) this.args = (_args = this.args).concat.apply(_args, arguments);

  return this;
}, '\nwithArgs is deprecated; use a closure instead.\n\n  expect(fn).withArgs(a, b, c).toThrow()\n\nbecomes\n\n  expect(() => fn(a, b, c)).toThrow()\n');

var aliases = {
  toBeAn: 'toBeA',
  toNotBeAn: 'toNotBeA',
  toBeTruthy: 'toExist',
  toBeFalsy: 'toNotExist',
  toBeFewerThan: 'toBeLessThan',
  toBeMoreThan: 'toBeGreaterThan',
  toContain: 'toInclude',
  toNotContain: 'toExclude',
  toNotInclude: 'toExclude',
  toContainKeys: 'toIncludeKeys',
  toNotContainKeys: 'toExcludeKeys',
  toNotIncludeKeys: 'toExcludeKeys',
  toContainKey: 'toIncludeKey',
  toNotContainKey: 'toExcludeKey',
  toNotIncludeKey: 'toExcludeKey'
};

for (var alias in aliases) {
  if (aliases.hasOwnProperty(alias)) Expectation.prototype[alias] = Expectation.prototype[aliases[alias]];
}exports.default = Expectation;