UNPKG

must

Version:

Testing and assertion library with friendly BDD syntax — awesome.must.be.true(). Many expressive matchers and is test runner and framework agnostic. Follows RFC 2119 with its use of MUST. Good and well tested stuff.

1,289 lines (1,185 loc) 34.7 kB
var O = require("oolong") var AssertionError = require("./lib/assertion_error") var Resolvable = require("./lib/resolvable") var Rejectable = require("./lib/rejectable") var kindof = require("kindof") var egal = require("egal") var deepEgal = egal.deepEgal var stringify = require("./lib").stringify var chain = require("./lib").chain var defineGetter = O.defineGetter var lookupGetter = O.lookupGetter var startsWith = require("./lib/es6").startsWith var endsWith = require("./lib/es6").endsWith var hasOwn = Function.call.bind(Object.hasOwnProperty) var ANY = {} exports = module.exports = Must exports.AssertionError = AssertionError exports.stringify = stringify exports.chain = chain /** * The main class that wraps the asserted object and that you call matchers on. * * To include a custom error message for failure cases, pass a string as the * second argument. * * Most of the time you'll be using * [`Object.prototype.must`](#Object.prototype.must) to create this wrapper, but * occasionally you might want to assert `null`s or `undefined`s and in those * cases assigning `Must` to something like `expect` or `demand` works nicely. * * @example * true.must.be.true() * [].must.be.empty() * * var expect = require("must") * expect(null).to.be.null() * * var demand = require("must") * demand(undefined, "The undefined undefineds").be.undefined() * * @class Must * @constructor * @param actual * @param [message] */ function Must(actual, message) { if (!(this instanceof Must)) return new Must(actual, message) this.actual = actual if (message != null) this.message = message } /** * Can also be used a pass-through property for a fluent chain. * * @example * "Hello".must.be.a.string() * new Date().must.be.a(Date) * * @method a * @alias instanceof */ defineGetter(Must.prototype, "a", function() { return chain(this, this.instanceof) }) /** * Can also be used a pass-through property for a fluent chain. * * @example * [1, 2].must.be.an.array() * new AwesomeClass().must.be.an(AwesomeClass) * * @method an * @alias instanceof */ defineGetter(Must.prototype, "an", lookupGetter(Must.prototype, "a")) /** * Pass-through property for a fluent chain. * * @example * (42).must.be.at.most(69) * (1337).must.be.at.least(1337) * * @property at * @on prototype */ defineGetter(Must.prototype, "at", passthrough) /** * Can also be used as a pass-through property for a fluent chain. * * @example * true.must.be.true() * (42).must.be(42) * * @method be * @alias equal */ defineGetter(Must.prototype, "be", function() { return chain(this, this.equal) }) /** * Pass-through property for a fluent chain. * * @example * [1, 2].must.have.length(2) * * @property have * @on prototype */ defineGetter(Must.prototype, "have", passthrough) /** * Inverse the assertion. * Use it multiple times to create lots of fun! * `true.must.not.not.be.true()` :-) * * @example * true.must.not.be.true() * [].must.not.be.empty() * * @property not * @on prototype */ defineGetter(Must.prototype, "not", function() { // NOTE: Dear reader or plugin author, please don't depend on this property // name will remain as-is. If you really need to, let me know how you'd like // to use it. XO. var self = Object.create(this) self.negative = !self.negative return self }) /** * Pass-through property for a fluent chain. * * @example * var expect = require("must") * expect(true).to.be.true() * * var wish = require("must") * wish(life).to.be.truthy() * * @property to * @on prototype */ defineGetter(Must.prototype, "to", passthrough) /** * Assert object is `true`. * A boxed boolean object (`new Boolean(true`) is _not_ considered true. * * @example * true.must.be.true() * * @method true */ Must.prototype.true = function() { this.assert(this.actual === true, "be", {expected: true}) } /** * Assert object is `false`. * A boxed boolean object (`new Boolean(false`) is _not_ considered false. * * @example * false.must.be.false() * @method false * */ Must.prototype.false = function() { this.assert(this.actual === false, "be", {expected: false}) } /** * Assert object is `NaN`. * * @example * NaN.must.be.nan() * * @method nan */ Must.prototype.nan = function() { this.assert(this.actual !== this.actual, "be", {expected: NaN}) } /** * Assert object is `null`. * * Because JavaScript does not allow method calls on `null`, you'll have to * wrap an expected null with [`Must`](#Must). Assigning `require("must")` to * `expect` or `demand` works well. * * If you want to assert that an object's property is `null`, see * [`property`](#Must.prototype.property). * * @example * var demand = require("must") * demand(null).be.null() * * @method null */ Must.prototype.null = function() { this.assert(this.actual === null, "be", {expected: null}) } /** * Assert object is `undefined`. * * Because JavaScript does not allow method calls on `undefined`, you'll have to * wrap an expected undefined with [`Must`](#Must). Assigning `require("must")` * to `expect` or `demand` works well. * * If you want to assert that an object's property is `undefined`, see * [`property`](#Must.prototype.property). * * @example * var demand = require("must") * demand(undefined).be.undefined() * * @method undefined */ Must.prototype.undefined = function() { this.assert(this.actual === undefined, "be", {expected: undefined}) } /** * Assert object is a boolean (`true` or `false`). * Boxed boolean objects (`new Boolean`) are _not_ considered booleans. * * @example * true.must.be.a.boolean() * * @method boolean */ Must.prototype.boolean = function() { this.assert(typeof this.actual == "boolean", "be a boolean") } /** * Assert object is a number. * Boxed number objects (`new Number`) are _not_ considered numbers. * * @example * (42).must.be.a.number() * * @method number */ Must.prototype.number = function() { this.assert(typeof this.actual == "number", "be a number") } /** * Assert object is a string. * Boxed string objects (`new String`) are _not_ considered strings. * * @example * "Hello".must.be.a.string() * * @method string */ Must.prototype.string = function() { this.assert(typeof this.actual == "string", "be a string") } /** * Assert object is a symbol. * * @example * Symbol().must.be.a.symbol() * * @method symbol */ Must.prototype.symbol = function() { this.assert(typeof this.actual == "symbol", "be a symbol") } /** * Assert object is a date. * * @example * new Date().must.be.a.date() * * @method date */ Must.prototype.date = function() { this.assert(kindof(this.actual) == "date", "be a date") } /** * Assert object is a regular expression. * * @example * /[a-z]/.must.be.a.regexp() * * @method regexp */ Must.prototype.regexp = function() { this.assert(kindof(this.actual) == "regexp", "be a regular expression") } /** * Assert object is an array. * * @example * [42, 69].must.be.an.array() * * @method array */ Must.prototype.array = function() { this.assert(Array.isArray(this.actual), "be an array") } /** * Assert object is a function. * * @example * (function() {}).must.be.a.function() * * @method function */ Must.prototype.function = function() { this.assert(typeof this.actual == "function", "be a function") } /** * Assert object is an.. object. * * @example * ({}).must.be.an.object() * * @method object */ Must.prototype.object = function() { var ok = this.actual && typeof this.actual == "object" this.assert(ok, "be an object") } /** * Assert object is truthy (`!!obj`). * * Only `null`, `undefined`, `0`, `false` and `""` are falsy in JavaScript. * Everything else is truthy. * * @example * (42).must.be.truthy() * "Hello".must.be.truthy() * * @method truthy */ Must.prototype.truthy = function() { this.assert(this.actual, "be truthy") } /** * Assert object is falsy (`!obj`). * * Only `null`, `undefined`, `0`, `false` and `""` are falsy in JavaScript. * Everything else is truthy. * * @example * 0.must.be.falsy() * "".must.be.falsy() * * @method falsy */ Must.prototype.falsy = function() { this.assert(!this.actual, "be falsy") } /** * Assert object is exists and thereby is not null or undefined. * * @example * 0.must.exist() * "".must.exist() * ({}).must.exist() * * @method exist */ Must.prototype.exist = function() { this.assert(this.actual != null, "exist") } /** * Assert that an object is an instance of something. * Uses `obj instanceof expected`. * * @example * new Date().must.be.an.instanceof(Date) * * @method instanceof * @param class */ Must.prototype.instanceof = function(expected) { var ok = this.actual instanceof expected this.assert(ok, instanceofMessage.bind(this, expected), {expected: expected}) } function instanceofMessage(expected) { var type = expected.displayName || expected.name || stringify(expected) return "be an instance of " + type } /** * @method instanceOf * @alias instanceof */ Must.prototype.instanceOf = Must.prototype.instanceof /** * Assert that an object is empty. * Checks either the `length` for arrays and strings or the count of * enumerable keys. Inherited keys also counted. * * @example * "".must.be.empty() * [].must.be.empty() * ({}).must.be.empty() * * @method empty */ Must.prototype.empty = function() { var ok = false if (typeof this.actual === "string" || Array.isArray(this.actual)) ok = this.actual.length === 0 else if (typeof this.actual == "object" || typeof this.actual == "function") ok = O.isEmpty(this.actual) this.assert(ok, "be empty") } /** * Assert a string ends with the given string. * * @example * "Hello, John".must.endWith("John") * * @method endWith * @param expected */ Must.prototype.endWith = function(expected) { this.assert(endsWith(this.actual, expected), "end with", {expected: expected}) } /** * Assert object strict equality or identity (`===`). * * To compare value objects (like `Date` or `RegExp`) by their value rather * than identity, use [`eql`](#Must.prototype.eql). * To compare arrays and objects by content, also use * [`eql`](#Must.prototype.eql). * * @example * (42).must.equal(42) * * var date = new Date * date.must.equal(date) * * @method equal * @param expected */ Must.prototype.equal = function(expected) { this.assert(this.actual === expected, "equal", {expected: expected}) } /** * Assert that an object is an error (instance of `Error` by default). * Optionally assert it matches `expected` (and/or is of instance * `constructor`). * When you have a function that's supposed to throw, use * [`throw`](#Must.prototype.throw). * * Given `expected`, the error is asserted as follows: * - A **string** is compared with the exception's `message` property. * - A **regular expression** is matched against the exception's `message` * property. * - A **function** (a.k.a. constructor) is used to check if the error * is an `instanceof` that constructor. * - All other cases of `expected` are left unspecified for now. * * @example * var err = throw new RangeError("Everything's amazing and nobody's happy") } * err.must.be.an.error() * err.must.be.an.error("Everything's amazing and nobody's happy") * err.must.be.an.error(/amazing/) * err.must.be.an.error(Error) * err.must.be.an.error(RangeError) * err.must.be.an.error(RangeError, "Everything's amazing and nobody's happy") * err.must.be.an.error(RangeError, /amazing/) * * @method error * @param [constructor] * @param [expected] */ Must.prototype.error = function(type, expected) { if (arguments.length <= 1) expected = ANY if (arguments.length == 1 && !isFn(type)) { expected = type; type = null } var ok = isError(this.actual, type || Error, expected) var msg = expected !== ANY ? "be an error matching" : "be an error" var opts = expected !== ANY ? {expected: expected} : null this.assert(ok, msg, opts) } /** * Can also be used as a pass-through property for a fluent chain. * * @example * var claim = require("must") * claim(true).is.true() * claim(42).is(42) * * @method is * @alias equal */ defineGetter(Must.prototype, "is", lookupGetter(Must.prototype, "be")) /** * Assert object equality by content and if possible, recursively. * Also handles circular and self-referential objects. * * For most parts it asserts strict equality (`===`), but: * - `RegExp` objects are compared by their pattern and flags. * - `Date` objects are compared by their value. * - `Array` objects are compared recursively. * - `NaN`s are considered equivalent. * - Instances of the same class with a `valueOf` function are compared by its * output. * - Plain objects and instances of the same class are compared recursively. * * **Does not coerce types** so **mismatching types fail**. * Inherited enumerable properties are also taken into account. * * **Instances** are objects whose prototype's `constructor` property is set. * E.g. `new MyClass`. * Others, like `{}` or `Object.create({})`, are **plain objects**. * * @example * /[a-z]/.must.eql(/[a-z]/) * new Date(1987, 5, 18).must.eql(new Date(1987, 5, 18)) * ["Lisp", 42].must.eql(["Lisp", 42]) * ({life: 42, love: 69}).must.eql({life: 42, love: 69}) * NaN.must.eql(NaN) * * function Answer(answer) { this.answer = answer } * new Answer(42).must.eql(new Answer(42)) * * @method eql * @param expected */ Must.prototype.eql = function(expected) { var ok = deepEgal(this.actual, expected, eql) this.assert(ok, "be equivalent to", {expected: expected, diffable: true}) } /** * Assert object includes `expected`. * * For strings it checks the text, for arrays it checks elements and for * objects the property values. Everything is checked with strict equals * (`===`). * * @example * "Hello, John!".must.include("John") * [1, 42, 3].must.include(42) * ({life: 42, love: 69}).must.include(42) * * @method include * @param expected */ Must.prototype.include = function(expected) { var found if (typeof this.actual === "string" || Array.isArray(this.actual)) found = this.actual.indexOf(expected) >= 0 else for (var key in this.actual) if (this.actual[key] === expected) { found = true; break } this.assert(found, "include", {expected: expected}) } /** * @method contain * @alias include */ Must.prototype.contain = Must.prototype.include /** * Assert that an array is a permutation of the given array. * * An array is a permutation of another if they both have the same elements * (including the same number of duplicates) regardless of their order. * Elements are checked with strict equals (`===`). * * @example * [1, 1, 2, 3].must.be.a.permutationOf([3, 2, 1, 1]) * [7, 8, 8, 9].must.not.be.a.permutationOf([9, 8, 7]) * * @method permutationOf * @param expected */ Must.prototype.permutationOf = function(expected) { var ok = isPermutationOf(this.actual, expected) this.assert(ok, "be a permutation of", {expected: expected, diffable: true}) } function isPermutationOf(actual, expected) { if (!Array.isArray(actual) || !Array.isArray(expected)) return false if (actual.length !== expected.length) return false actual = actual.slice().sort() expected = expected.slice().sort() for (var i = 0; i < actual.length; i++) { if (actual[i] !== expected[i]) return false } return true } /** * Assert object matches the given regular expression. * * If you pass in a non regular expression object, it'll be converted to one * via `new RegExp(regexp)`. * * @example * "Hello, John!".must.match(/john/i) * "Wei wu wei".must.match("wu") * * @method match * @param regexp */ Must.prototype.match = function(expected) { var regexp = expected instanceof RegExp ? expected : new RegExp(expected) this.assert(regexp.exec(this.actual), "match", {expected: regexp}) } /** * Pass-through property for a fluent chain. * * @example * (42).must.must.must.must.equal(42) * * @property must * @on prototype */ defineGetter(Must.prototype, "must", passthrough) /** * Pass-through property for a fluent chain. * * @example * (42).must.be.the.number() * * @property the * @on prototype */ defineGetter(Must.prototype, "the", passthrough) /** * Assert that a function throws. * Optionally assert it throws `expected` (and/or is of instance * `constructor`). * When you already have an error reference, use * [`error`](#Must.prototype.error). * * Given `expected`, the error is asserted as follows: * - A **string** is compared with the exception's `message` property. * - A **regular expression** is matched against the exception's `message` * property. * - A **function** (a.k.a. constructor) is used to check if the error * is an `instanceof` that constructor. * - All other cases of `expected` are left unspecified for now. * * Because of how JavaScript works, the function will be called in `null` * context (`this`). If you want to test an instance method, bind it: * `obj.method.bind(obj).must.throw()`. * * @example * function omg() { * throw new RangeError("Everything's amazing and nobody's happy") * } * * omg.must.throw() * omg.must.throw("Everything's amazing and nobody's happy") * omg.must.throw(/amazing/) * omg.must.throw(Error) * omg.must.throw(RangeError) * omg.must.throw(RangeError, "Everything's amazing and nobody's happy") * omg.must.throw(RangeError, /amazing/) * * @method throw * @param [constructor] * @param [expected] */ Must.prototype.throw = function(type, expected) { if (arguments.length <= 1) expected = ANY if (arguments.length == 1 && !isFn(type)) { expected = type; type = null } var ok = false, exception try { this.actual.call(null) } catch (ex) { ok = true; exception = ex } ok = ok && isError(exception, type, expected) var opts = {actual: exception} if (expected !== ANY) opts.expected = expected this.assert(ok, "throw", opts) } /** * Assert that an object has a length property equal to `expected`. * * @example * "Something or other".must.have.length(18) * [1, 2, 3, "Four o'clock rock"].must.have.length(4) * * @method length * @param expected */ Must.prototype.length = function(expected) { var ok = this.actual.length == expected this.assert(ok, "have length of", {expected: expected}) } /** * Assert that an object is frozen with `Object.isFrozen`. * * @example * Object.freeze({}).must.be.frozen() * * @method frozen */ Must.prototype.frozen = function() { this.assert(Object.isFrozen(this.actual), "be frozen") } /** * Assert that an object has all of the properties given in `properties` with * equal (`===`) values. In other words, asserts that the given object is * a subset of the one asserted against. * * Takes **inherited properties** into account. To not do so, see * [`ownProperties`](#Must.prototype.ownProperties). * * @example * var john = {name: "John", age: 42, sex: "male"} * john.must.have.properties({name: "John", sex: "male"}) * * @method properties * @param properties */ Must.prototype.properties = function(props) { var obj = this.actual var ok = this.actual != null if (ok) for (var key in props) { ok = key in obj && obj[key] === props[key] if (!ok) break } this.assert(ok, "have properties", {expected: props, diffable: true}) } /** * Assert that an object has all of the properties given in `properties` with * equal (`===`) values and that they're own properties. In other words, * asserts that the given object is a subset of the one asserted against. * * **Does not** take **inherited properties** into account. To do so, see * [`properties`](#Must.prototype.properties). * * @example * var john = {name: "John", age: 42, sex: "male"} * john.must.have.ownProperties({name: "John", sex: "male"}) * * @method ownProperties * @param properties */ Must.prototype.ownProperties = function(props) { var obj = this.actual var ok = this.actual != null if (ok) for (var key in props) { ok = key in obj && hasOwn(obj, key) && obj[key] === props[key] if (!ok) break } this.assert(ok, "have own properties", {expected: props, diffable: true}) } /** * Assert that an object has property `property`. * Optionally assert it *equals* (`===`) to `value`. * * Takes **inherited properties** into account. To not do so, see * [`ownProperty`](#Must.prototype.ownProperty). * * @example * (function() {}).must.have.property("call") * ({life: 42, love: 69}).must.have.property("love", 69) * * @method property * @param property * @param [value] */ Must.prototype.property = function(property, expected) { var ok = this.actual != null && property in Object(this.actual) if (ok && arguments.length > 1) ok = this.actual[property] === expected var msg = "have property \"" + property + "\"", opts if (arguments.length > 1) { msg += " equal to"; opts = {expected: expected} } this.assert(ok, msg, opts) } /** * Assert that an object has own property `property`. * Optionally assert it *equals* (`===`) to `value`. * * **Does not** take **inherited properties** into account. To do so, see * [`property`](#Must.prototype.property). * * @example * ({life: 42, love: 69}).must.have.ownProperty("love", 69) * * @method ownProperty * @param property * @param [value] */ Must.prototype.ownProperty = function(property, expected) { var ok = this.actual != null ok = ok && hasOwn(this.actual, property) if (ok && arguments.length > 1) ok = this.actual[property] === expected var msg = "have own property \"" + property + "\"", opts if (arguments.length > 1) { msg += " equal to"; opts = {expected: expected} } this.assert(ok, msg, opts) } /** * @method own * @alias ownProperty */ Must.prototype.own = Must.prototype.ownProperty /** * Assert that an object has only the expected enumerable `keys`. * Pass an array of strings as `keys`. * * Takes **inherited properties** into account. To not do so, see * [`ownKeys`](#Must.prototype.ownKeys). * * @example * ({life: 42, love: 69}).must.have.keys(["life", "love"]) * Object.create({life: 42}).must.have.keys(["life"]) * * @method keys * @param keys */ Must.prototype.keys = function(expected) { var ok = this.actual != null ok = ok && isPermutationOf(O.keys(Object(this.actual)), expected) this.assert(ok, "have keys", {expected: expected}) } /** * Assert that an object has only the expected enumerable `keys` of its own. * Pass an array of strings as `keys`. * * **Does not** take **inherited properties** into account. To do so, see * [`keys`](#Must.prototype.keys). * * @example * ({life: 42, love: 69}).must.have.ownKeys(["life", "love"]) * * @method ownKeys * @param keys */ Must.prototype.ownKeys = function(expected) { var ok = this.actual != null ok = ok && isPermutationOf(Object.keys(Object(this.actual)), expected) this.assert(ok, "have own keys", {expected: expected}) } /** * Assert that an object has an enumerable property `property`. * It will fail if the object lacks the property entirely. * * This also checks inherited properties in the prototype chain, something which * `Object.prototype.propertyIsEnumerable` itself does not do. * * For checking if a property exists *and* is non-enumerable, see * [`nonenumerable`](#Must.prototype.nonenumerable). * * @example * ({life: 42, love: 69}).must.have.enumerable("love") * * @method enumerable * @param property */ Must.prototype.enumerable = function(property) { var ok = this.actual != null ok = ok && isEnumerable(Object(this.actual), property) this.assert(ok, "have enumerable property \"" + property + "\"") } /** * @method enumerableProperty * @alias enumerable */ Must.prototype.enumerableProperty = Must.prototype.enumerable /** * Assert that an object has a non-enumerable property `property`. * It will fail if the object lacks the property entirely. * * This also checks inherited properties in the prototype chain, something which * `Object.prototype.propertyIsEnumerable` itself does not do. * * It's the inverse of [`enumerable`](#Must.prototype.enumerable). * * @example * (function() {}).must.have.nonenumerable("call") * Object.create({}, {love: {enumerable: 0}}).must.have.nonenumerable("love") * * @method nonenumerable * @param property */ Must.prototype.nonenumerable = function(property) { var ok = this.actual != null ok = ok && property in Object(this.actual) ok = ok && !isEnumerable(Object(this.actual), property) this.assert(ok, "have nonenumerable property \"" + property + "\"") } function isEnumerable(obj, name) { // Using propertyIsEnumerable saves a possible looping of all keys. if (Object.prototype.propertyIsEnumerable.call(obj, name)) return true for (var key in obj) if (key == name) return true return false } /** * @method nonenumerableProperty * @alias nonenumerable */ Must.prototype.nonenumerableProperty = Must.prototype.nonenumerable /** * Assert that an object is below and less than (`<`) `expected`. * Uses `<` for comparison, so it'll also work with value objects (those * implementing [`valueOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf)) like `Date`. * * @example * (42).must.be.below(69) * * @method below * @param expected */ Must.prototype.below = function(expected) { this.assert(this.actual < expected, "be below", {expected: expected}) } /** * @method lt * @alias below */ Must.prototype.lt = Must.prototype.below /** * Works well with dates where saying *before* is more natural than *below* or * *less than*. * * To assert that a date is equivalent to another date, use * [`eql`](#Must.prototype.eql). For regular numbers, * [`equal`](#Must.prototype.equal) is fine. * * @example * (42).must.be.before(1337) * new Date(2000, 5, 18).must.be.before(new Date(2001, 0, 1)) * * @method before * @alias below */ Must.prototype.before = Must.prototype.below /** * Assert that an object is at most, less than or equal to (`<=`), `expected`. * Uses `<=` for comparison, so it'll also work with value objects (those * implementing [`valueOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf)) like `Date`. * * @example * (42).must.be.at.most(69) * (42).must.be.at.most(42) * * @method most * @param expected */ Must.prototype.most = function(expected) { this.assert(this.actual <= expected, "be at most", {expected: expected}) } /** * @method lte * @alias most */ Must.prototype.lte = Must.prototype.most /** * Assert that an object is above and greater than (`>`) `expected`. * Uses `>` for comparison, so it'll also work with value objects (those * implementing [`valueOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf)) like `Date`. * * @example * (69).must.be.above(42) * * @method above * @param expected */ Must.prototype.above = function(expected) { this.assert(this.actual > expected, "be above", {expected: expected}) } /** * @method gt * @alias above */ Must.prototype.gt = Must.prototype.above /** * Works well with dates where saying *after* is more natural than *above* or * *greater than*. * * To assert that a date is equivalent to another date, use * [`eql`](#Must.prototype.eql). For regular numbers, * [`equal`](#Must.prototype.equal) is fine. * * @example * (1337).must.be.after(42) * new Date(2030, 5, 18).must.be.after(new Date(2013, 9, 23)) * * @method after * @alias above */ Must.prototype.after = Must.prototype.above /** * Assert that an object is at least, greater than or equal to (`>=`), * `expected`. * Uses `>=` for comparison, so it'll also work with value objects (those * implementing [`valueOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf)) like `Date`. * * @example * (69).must.be.at.least(42) * (42).must.be.at.least(42) * * @method least * @param expected */ Must.prototype.least = function(expected) { this.assert(this.actual >= expected, "be at least", {expected: expected}) } /** * @method gte * @alias least */ Must.prototype.gte = Must.prototype.least /** * Assert that an object is between `begin` and `end` (inclusive). * Uses `<` for comparison, so it'll also work with value objects (those * implementing [`valueOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf)) like `Date`. * * @example * (13).must.be.between(13, 69) * (42).must.be.between(13, 69) * (69).must.be.between(13, 69) * * @method between * @param begin * @param end */ Must.prototype.between = function(begin, end) { this.assert(begin <= this.actual && this.actual <= end, function() { return "be between " + stringify(begin) + " and " + stringify(end) }) } /** * Makes any matcher following the use of `resolve` wait till a promise * resolves before asserting. * Returns a new promise that will either resolve if the assertion passed or * fail with `AssertionError`. * * Promises are transparent to matchers, so everything will also work with * customer matchers you've added to `Must.prototype`. Internally Must just * waits on the promise and calls the matcher function once it's resolved. * * With [Mocha](http://mochajs.org), using this will look something like: * * ```javascript * it("must pass", function() { * return Promise.resolve(42).must.resolve.to.equal(42) * }) * ``` * * Using [CoMocha](https://github.com/blakeembrey/co-mocha), it'll look like: * ```javascript * it("must pass", function*() { * yield Promise.resolve(42).must.resolve.to.equal(42) * yield Promise.resolve([1, 2, 3]).must.resolve.to.not.include(42) * }) * ``` * * @example * Promise.resolve(42).must.resolve.to.equal(42) * Promise.resolve([1, 2, 3]).must.resolve.to.not.include(42) * * @property resolve * @on prototype */ defineGetter(Must.prototype, "resolve", function() { return Resolvable(this) }) /** * @example * Promise.resolve(42).must.then.equal(42) * * @property then * @on prototype * @alias resolve */ defineGetter(Must.prototype, "then", lookupGetter(Must.prototype, "resolve")) /** * @example * Promise.resolve(42).must.eventually.equal(42) * * @property eventually * @on prototype * @alias resolve */ defineGetter(Must.prototype, "eventually", lookupGetter(Must.prototype, "resolve")) /** * Makes any matcher following the use of `reject` wait till a promise * is rejected before asserting. * Returns a new promise that will either resolve if the assertion passed or * fail with `AssertionError`. * * Promises are transparent to matchers, so everything will also work with * customer matchers you've added to `Must.prototype`. Internally Must just * waits on the promise and calls the matcher function once it's rejected. * * With [Mocha](http://mochajs.org), using this will look something like: * * ```javascript * it("must pass", function() { * return Promise.reject(42).must.reject.to.equal(42) * }) * ``` * * Using [CoMocha](https://github.com/blakeembrey/co-mocha), it'll look like: * ```javascript * it("must pass", function*() { * yield Promise.reject(42).must.reject.to.equal(42) * yield Promise.reject([1, 2, 3]).must.reject.to.not.include(42) * }) * ``` * * @example * Promise.reject(42).must.reject.to.equal(42) * Promise.reject([1, 2, 3]).must.reject.to.not.include(42) * * @property reject * @on prototype */ defineGetter(Must.prototype, "reject", function() { return Rejectable(this) }) /** * Assert a string starts with the given string. * * @example * "Hello, John".must.startWith("Hello") * * @method startWith * @param expected */ Must.prototype.startWith = function(expected) { var ok = startsWith(this.actual, expected) this.assert(ok, "start with", {expected: expected}) } /** * Pass-through property for a fluent chain. * * @example * Promise.resolve(42).must.resolve.with.number() * * @property with * @on prototype */ defineGetter(Must.prototype, "with", passthrough) Must.prototype.assert = function assert(ok, message, opts) { if (!this.negative ? ok : !ok) return opts = opts ? Object.create(opts) : {} if (!("actual" in opts)) opts.actual = this.actual if (!("caller" in opts)) { // Accessing caller in strict mode throws TypeError. try { opts.caller = assert.caller } catch (ex) { opts.caller = assert } } var msg = stringify(this.actual) + " must " + (this.negative ? "not " : "") if (typeof message == "function") msg += message.call(this) else msg += message + ("expected" in opts ? " "+stringify(opts.expected) : "") if (this.message != null) msg = this.message + ": " + msg throw new AssertionError(msg, opts) } Object.defineProperty(Must.prototype, "assert", {enumerable: false}) function eql(a, b) { if (egal(a, b)) return true var type = kindofPlain(a) if (type !== kindofPlain(b)) return false if (isNumber(a) && isNumber(b) && isNaN(+a) && isNaN(+b)) return true switch (type) { case "array": case "plain": return null case "object": if (getConstructorOf(a) !== getConstructorOf(b)) return false if (hasValueOf(a) && hasValueOf(b)) return false return null default: return false } } function getConstructorOf(obj) { var prototype = Object.getPrototypeOf(obj) return prototype === null ? undefined : prototype.constructor } function hasValueOf(obj) { var valueOf = obj.valueOf return typeof valueOf === "function" && valueOf !== Object.prototype.valueOf } function kindofPlain(obj) { var type = kindof(obj) if (type === "object" && O.isPlainObject(obj)) return "plain" return type } function isError(err, constructor, expected) { if (constructor != null && !(err instanceof constructor)) return false if (expected === ANY) return true switch (kindof(expected)) { case "string": return messageFromError(err) === expected case "regexp": return expected.exec(messageFromError(err)) default: return err === expected } } function messageFromError(err) { // The message in new Error(message) gets converted to a string. return err == null || typeof err == "string" ? err : err.message } function isFn(fn) { return typeof fn === "function" } function isNumber(n) { return typeof n === "number" || n instanceof Number } function passthrough() { return this }