From 77ca05763d097b8a0ed6272a296531faddbe9758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Sun, 28 Jan 2024 16:57:22 +0100 Subject: [PATCH] Implement `iterable` assertion --- lib/chai/core/assertions.js | 37 ++++++++++++++++++++++++++++++++++- lib/chai/interface/assert.js | 18 ++++++++++++++++- test/assert.js | 34 ++++++++++++++++++++++++++++++-- test/expect.js | 38 ++++++++++++++++++++++++++++++++---- test/should.js | 28 +++++++++++++++++++++++--- 5 files changed, 144 insertions(+), 11 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 0a55c58e..393ce217 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -319,6 +319,12 @@ function an (type, msg) { , 'expected #{this} to be ' + article + type , 'expected #{this} not to be ' + article + type ); + } else if (type === 'iterable') { + this.assert( + typeof obj !== 'string' && obj != undefined && obj[Symbol.iterator] + , 'expected #{this} to be ' + article + type + , 'expected #{this} not to be ' + article + type + ); } else { this.assert( type === detectedType @@ -3133,7 +3139,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') @@ -3170,6 +3175,36 @@ Assertion.addMethod('members', function (subset, msg) { ); }); +/** + * ### .iterable + * + * Asserts that the target is an iterable, which means that it has a iterator + * with the exception of `String.` + * + * expect([1, 2]).to.be.iterable; + * + * Add `.not` earlier in the chain to negate `.iterable`. + * + * expect(1).to.not.be.iterable; + * expect("foobar").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') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + new Assertion(obj, flagMsg, ssfi, true).to.be.an('iterable'); +}); + /** * ### .oneOf(list[, msg]) * diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index 24ceff36..b59d22fd 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -8,7 +8,6 @@ import * as chai from '../../../index.js'; import {Assertion} from '../assertion.js'; import {flag, inspect} from '../utils/index.js'; import {AssertionError} from 'assertion-error'; -import {type} from '../utils/type-detect.js'; /** * ### assert(expression, message) @@ -2485,6 +2484,23 @@ 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) { + new Assertion(obj, msg, assert.isIterable, true).to.be.an('iterable'); +} + /** * ### .changes(function, object, property, [message]) * diff --git a/test/assert.js b/test/assert.js index 22373a35..b1a7b836 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1979,11 +1979,11 @@ describe('assert', function () { err(function () { assert.sameMembers({}, [], 'blah'); - }, 'blah: expected {} to be an array'); + }, 'blah: expected {} to be an iterable'); err(function () { assert.sameMembers([], {}, 'blah'); - }, 'blah: expected {} to be an array'); + }, 'blah: expected {} to be an iterable'); }); it('notSameMembers', function() { @@ -2375,6 +2375,36 @@ 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])); + + err(function() { + assert.isIterable(42); + }, 'expected 42 to be an iterable'); + + err(function() { + assert.isIterable('hello'); + }, "expected 'hello' 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'], diff --git a/test/expect.js b/test/expect.js index d5247655..28ace99a 100644 --- a/test/expect.js +++ b/test/expect.js @@ -3508,19 +3508,19 @@ describe('expect', function () { err(function () { expect({}).members([], 'blah'); - }, 'blah: expected {} to be an array'); + }, 'blah: expected {} to be an iterable'); err(function () { expect({}, 'blah').members([]); - }, 'blah: expected {} to be an array'); + }, 'blah: expected {} to be an iterable'); err(function () { expect([]).members({}, 'blah'); - }, 'blah: expected {} to be an array'); + }, 'blah: expected {} to be an iterable'); err(function () { expect([], 'blah').members({}); - }, 'blah: expected {} to be an array'); + }, 'blah: expected {} to be an iterable'); }); it('deep.members', function() { @@ -3641,6 +3641,36 @@ 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; + + err(function() { + expect(42).to.be.iterable; + }, 'expected 42 to be an iterable'); + + err(function() { + expect('hello').to.be.iterable; + }, "expected 'hello' 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'], diff --git a/test/should.js b/test/should.js index adb94d94..323b98c5 100644 --- a/test/should.js +++ b/test/should.js @@ -2820,11 +2820,11 @@ describe('should', function() { err(function() { 'foo'.should.include.members([12], 'blah'); - }, "blah: expected 'foo' to be an array"); + }, "blah: expected 'foo' to be an iterable"); err(function() { [1, 2, 3].should.include.members('o', 'blah'); - }, "blah: expected 'o' to be an array"); + }, "blah: expected 'o' to be an iterable"); }); it('memberEquals', function() { @@ -2845,7 +2845,7 @@ describe('should', function() { err(function() { [1, 2, 3].should.have.same.members(4, 'blah'); - }, 'blah: expected 4 to be an array'); + }, 'blah: expected 4 to be an iterable'); }); it('deep.members', function() { @@ -2942,6 +2942,28 @@ 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; + + err(function() { + (42).should.be.iterable; + }, 'expected 42 to be an iterable'); + + err(function() { + ('hello').should.be.iterable; + }, "expected 'hello' 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'],