Skip to content

Commit

Permalink
Implement iterable assertion (#1592)
Browse files Browse the repository at this point in the history
* Implement `iterator` assertion

* Consider `string` to be iterable

* Revert changes to `isSubsetOf`

* Move iterable check out of `an()` function
  • Loading branch information
Kristján Oddsson authored Feb 11, 2024
1 parent 640d932 commit d504573
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 1 deletion.
33 changes: 32 additions & 1 deletion lib/chai/core/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3133,7 +3133,6 @@ function isSubsetOf(subset, superset, cmp, contains, ordered) {
* @namespace BDD
* @api public
*/

Assertion.addMethod('members', function (subset, msg) {
if (msg) flag(this, 'message', msg);
var obj = flag(this, 'object')
Expand Down Expand Up @@ -3170,6 +3169,38 @@ Assertion.addMethod('members', function (subset, msg) {
);
});

/**
* ### .iterable
*
* Asserts that the target is an iterable, which means that it has a iterator.
*
* expect([1, 2]).to.be.iterable;
*
* Add `.not` earlier in the chain to negate `.iterable`.
*
* expect(1).to.not.be.iterable;
* expect(true).to.not.be.iterable;
*
* A custom error message can be given as the second argument to `expect`.
*
* expect(1, 'nooo why fail??').to.be.iterable;
*
* @name iterable
* @namespace BDD
* @api public
*/
Assertion.addProperty('iterable', function(msg) {
if (msg) flag(this, 'message', msg);
var obj = flag(this, 'object');

this.assert(
obj != undefined && obj[Symbol.iterator]
, 'expected #{this} to be an iterable'
, 'expected #{this} to not be an iterable'
, obj
);
});

/**
* ### .oneOf(list[, msg])
*
Expand Down
27 changes: 27 additions & 0 deletions lib/chai/interface/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -2376,6 +2376,33 @@ assert.oneOf = function (inList, list, msg) {
new Assertion(inList, msg, assert.oneOf, true).to.be.oneOf(list);
}

/**
* ### isIterable(obj, [message])
*
* Asserts that the target is an iterable, which means that it has a iterator
* with the exception of `String.`
*
* assert.isIterable([1, 2]);
*
* @param {unknown} obj
* @param {string} [msg]
* @namespace Assert
* @api public
*/
assert.isIterable = function(obj, msg) {
if (obj == undefined || !obj[Symbol.iterator]) {
msg = msg ?
`${msg} expected ${inspect(obj)} to be an iterable` :
`expected ${inspect(obj)} to be an iterable`;

throw new AssertionError(
msg,
undefined,
assert.isIterable
);
}
}

/**
* ### .changes(function, object, property, [message])
*
Expand Down
27 changes: 27 additions & 0 deletions test/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -2315,6 +2315,33 @@ describe('assert', function () {
}, 'blah: the argument to most must be a number');
});

it('iterable', function() {
assert.isIterable([1, 2, 3]);
assert.isIterable(new Map([[1, 'one'], [2, 'two'], [3, 'three']]));
assert.isIterable(new Set([1, 2, 3]));
assert.isIterable('hello');

err(function() {
assert.isIterable(42);
}, 'expected 42 to be an iterable');

err(function() {
assert.isIterable(undefined);
}, 'expected undefined to be an iterable');

err(function() {
assert.isIterable(null);
}, 'expected null to be an iterable');

err(function() {
assert.isIterable(true);
}, 'expected true to be an iterable');

err(function() {
assert.isIterable({ key: 'value' });
}, 'expected { key: \'value\' } to be an iterable');
});

it('change', function() {
var obj = { value: 10, str: 'foo' },
heroes = ['spiderman', 'superman'],
Expand Down
27 changes: 27 additions & 0 deletions test/expect.js
Original file line number Diff line number Diff line change
Expand Up @@ -3569,6 +3569,33 @@ describe('expect', function () {
}, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not be an ordered superset of [ { a: 1 }, { b: 2 } ]');
});

it('iterable', function() {
expect([1, 2, 3]).to.be.iterable;
expect(new Map([[1, 'one'], [2, 'two'], [3, 'three']])).to.be.iterable;
expect(new Set([1, 2, 3])).to.be.iterable;
expect('hello').to.be.iterable;

err(function() {
expect(42).to.be.iterable;
}, 'expected 42 to be an iterable');

err(function() {
expect(undefined).to.be.iterable;
}, 'expected undefined to be an iterable');

err(function() {
expect(null).to.be.iterable;
}, 'expected null to be an iterable');

err(function() {
expect(true).to.be.iterable;
}, 'expected true to be an iterable');

err(function() {
expect({ key: 'value' }).to.be.iterable;
}, 'expected { key: \'value\' } to be an iterable');
})

it('change', function() {
var obj = { value: 10, str: 'foo' },
heroes = ['spiderman', 'superman'],
Expand Down
19 changes: 19 additions & 0 deletions test/should.js
Original file line number Diff line number Diff line change
Expand Up @@ -2937,6 +2937,25 @@ describe('should', function() {
}, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not be an ordered superset of [ { a: 1 }, { b: 2 } ]');
});

it ('iterable', function() {
([1, 2, 3]).should.be.iterable;
(new Map([[1, 'one'], [2, 'two'], [3, 'three']])).should.be.iterable;
(new Set([1, 2, 3])).should.be.iterable;
('hello').should.be.iterable;

err(function() {
(42).should.be.iterable;
}, 'expected 42 to be an iterable');

err(function() {
(true).should.be.iterable;
}, 'expected true to be an iterable');

err(function() {
({ key: 'value' }).should.be.iterable;
}, 'expected { key: \'value\' } to be an iterable');
})

it('change', function() {
var obj = { value: 10, str: 'foo' },
heroes = ['spiderman', 'superman'],
Expand Down

0 comments on commit d504573

Please sign in to comment.