UNPKG

chai-iterator

Version:
335 lines (271 loc) 8.26 kB
(function (chaiIterator) { 'use strict'; /* istanbul ignore else */ if (typeof module === 'object' && typeof exports === 'object') { module.exports = chaiIterator; } else if (typeof define === 'function' && define.amd) { define(function () { return chaiIterator; }); } else { chai.use(chaiIterator); } })(function (chai, utils) { 'use strict'; var Assertion = chai.Assertion; var assert = chai.assert; var noop = function () {}; var ELLIPSIS = unquote('...'); // Expect/should assertions ============================= Assertion.addProperty('iterable', function () { this.assert(isIterable(this._obj), 'expected #{this} to be iterable', 'expected #{this} not to be iterable' ); }); Assertion.addProperty('iterate', function () { new Assertion(this._obj).iterable; utils.flag(this, 'iterate', true); }); Assertion.addMethod('over', iterateMethod('to iterate over', function (exp) { var it = this._obj[Symbol.iterator](); var actual = slice(it, exp.length + 2); var suffix = it.next().done ? [] : [ELLIPSIS]; return actual.concat(suffix); })); Assertion.addMethod('from', iterateMethod('to begin iteration with', function (exp) { return slice(this._obj[Symbol.iterator](), exp.length); })); Assertion.addMethod('until', iterateMethod('to end iteration with', function (exp) { var it = this._obj[Symbol.iterator](); var actual = slice(it, exp.length); var step = it.next(); while (!step.done) { actual.push(step.value); actual.shift(); step = it.next(); } return actual; })); Assertion.addProperty('for', noop); Assertion.overwriteChainableMethod('lengthOf', function (_super) { return function (exp) { if (utils.flag(this, 'iterate')) { var len = iterableLength(this._obj, exp); this.assert( len === exp, 'expected #{this} to iterate for length of #{exp}, but got #{act}', 'expected #{this} not to iterate for length of #{exp}', exp, len ); } else { _super.apply(this, arguments); } }; }, function (_super) { return function () { _super.apply(this); }; }); Assertion.overwriteMethod('above', function (_super) { return function (exp) { if (utils.flag(this, 'iterate')) { var len = iterableLength(this._obj, exp); this.assert( len > exp, 'expected #{this} to iterate for length above #{exp}, but got #{act}', 'expected #{this} not to iterate for length above #{exp}', exp, len ); } else { _super.apply(this, arguments); } }; }); Assertion.overwriteMethod('below', function (_super) { return function (exp) { if (utils.flag(this, 'iterate')) { var max = exp + 100; var len = iterableLength(this._obj, max); var act = len === Infinity ? unquote('more than ' + max) : len; this.assert( len < exp, 'expected #{this} to iterate for length below #{exp}, but got #{act}', 'expected #{this} not to iterate for length below #{exp}', exp, act ); } else { _super.apply(this, arguments); } }; }); Assertion.overwriteMethod('least', function (_super) { return function (exp) { if (utils.flag(this, 'iterate')) { var len = iterableLength(this._obj, exp); this.assert( len >= exp, 'expected #{this} to iterate for length of at least #{exp}, but got #{act}', 'expected #{this} not to iterate for length of at least #{exp}', exp, len ); } else { _super.apply(this, arguments); } }; }); Assertion.overwriteMethod('most', function (_super) { return function (exp) { if (utils.flag(this, 'iterate')) { var max = exp + 100; var len = iterableLength(this._obj, max); var act = len === Infinity ? unquote('more than ' + max) : len; this.assert( len <= exp, 'expected #{this} to iterate for length of at most #{exp}, but got #{act}', 'expected #{this} not to iterate for length of at most #{exp}', exp, act ); } else { _super.apply(this, arguments); } }; }); Assertion.overwriteMethod('within', function (_super) { return function (min, max) { if (utils.flag(this, 'iterate')) { var cutoff = max + 100; var len = iterableLength(this._obj, cutoff); var exp = unquote(min + 'and' + max); var act = len === Infinity ? unquote('more than ' + cutoff) : len; this.assert( min <= len && len <= max, 'expected #{this} to iterate for length within #{exp}, but got #{act}', 'expected #{this} not to iterate for length within #{exp}', exp, act ); } else { _super.apply(this, arguments); } }; }); // Assert methods ======================================= assert.isIterable = function (value, msg) { new Assertion(value, msg).iterable; }; assert.isNotIterable = function (value, msg) { new Assertion(value, msg).not.iterable; }; assert.iteratesFrom = function (value, exp, msg) { new Assertion(value, msg).iterate.from(exp); }; assert.doesNotIterateFrom = function (value, exp, msg) { new Assertion(value, msg).not.iterate.from(exp); }; assert.deepIteratesFrom = function (value, exp, msg) { new Assertion(value, msg).deep.iterate.from(exp); }; assert.doesNotDeepIterateFrom = function (value, exp, msg) { new Assertion(value, msg).not.deep.iterate.from(exp); }; assert.iteratesOver = function (value, exp, msg) { new Assertion(value, msg).iterate.over(exp); }; assert.doesNotIterateOver = function (value, exp, msg) { new Assertion(value, msg).not.iterate.over(exp); }; assert.deepIteratesOver = function (value, exp, msg) { new Assertion(value, msg).deep.iterate.over(exp); }; assert.doesNotDeepIterateOver = function (value, exp, msg) { new Assertion(value, msg).not.deep.iterate.over(exp); }; assert.iteratesUntil = function (value, exp, msg) { new Assertion(value, msg).iterate.until(exp); }; assert.doesNotIterateUntil = function (value, exp, msg) { new Assertion(value, msg).not.iterate.until(exp); }; assert.deepIteratesUntil = function (value, exp, msg) { new Assertion(value, msg).deep.iterate.until(exp); }; assert.doesNotDeepIterateUntil = function (value, exp, msg) { new Assertion(value, msg).not.deep.iterate.until(exp); }; var _lengthOf = assert.lengthOf; assert.lengthOf = function (value, exp, msg) { if (isIterable(value) && typeof value.length !== 'number') { new Assertion(value, msg).iterate.for.lengthOf(exp); } else { _lengthOf.apply(assert, arguments); } }; // Helpers ============================================== function iterateMethod(predicate, getActual) { return function (iterable) { assert(utils.flag(this, 'iterate'), 'the iterate flag must be set'); new Assertion(iterable).iterable; var exp = slice(iterable[Symbol.iterator]()); var act = getActual.call(this, exp); var deep = utils.flag(this, 'deep') ? ' deep' : ''; this.assert(compareArrays(exp, act, deep), 'expected #{this} ' + predicate + deep + ' values #{exp}, but got #{act}', 'expected #{this} not ' + predicate + deep + ' values #{exp}', exp, act ); }; } // Utilities ============================================ function isIterable(value) { return value !== undefined && value !== null && typeof value[Symbol.iterator] === 'function'; } function iterableLength(iterable, max) { max = max === undefined ? Infinity : max; var it = iterable[Symbol.iterator](); for (var i = 0; i <= max; i++) { if (it.next().done) { return i; } } return Infinity; } function strictEqual(a, b) { return a === b; } function compareArrays(exp, act, deep) { var equalFn = deep ? utils.eql : strictEqual; return exp.length === act.length && exp.every(function (value, i) { return equalFn(value, act[i]); }); } function slice(it, stop) { stop = stop === undefined ? Infinity : stop; var result = []; var max = stop - 1; var step = it.next(); for (var i = 0; i <= max && !step.done; i++) { result.push(step.value); if (i < max) { step = it.next(); } } return result; } function unquote(str) { return { inspect: function () { return this.toString(); }, toString: function () { return str; } }; } });