1. chai-iterator
Chai assertions for iterable objects
chai-iterator
Package: chai-iterator
Created by: harrysarson
Last modified: Mon, 13 Jun 2022 05:51:02 GMT
Version: 3.0.2
License: MIT
Downloads: 4,654
Repository: https://github.com/harrysarson/chai-iterator

Install

npm install chai-iterator
yarn add chai-iterator

chai-iterator: Assertions for iterable objects

Version
Build
Coverage
Dependencies

Contents

Overview

chai-iterator extends the Chai assertion library with methods for
testing iterable objects. Introduced in the
ES2015 specification, iterable objects have an
@@iterator method, which allows us to iterate over them with
a for...of loop. A number of built-in types are
iterable by default, while custom iterable objects may also
be defined. chai-iterator makes it easy to test all such objects.

You may not need chai-iterator

In many cases the array spread operator is the best way to test iterables.
chai-iterator is however very useful for testing part of a very long (or infinite) iterable.

Basic usage

Here is a fairly exhaustive sample of the assertions we can make using Chai
Iterator. While we could just as easily use expect or assert, we'll use
Chai's should() assertion style, just to be different.

 [2, 3, 5].should.be.iterable;

[2, 3, 5].should.iterate.over([2, 3, 5]);
[2, 3, 5].should.iterate.from([2, 3]);
[2, 3, 5].should.iterate.until([3, 5]);

[2, 3, 5].should.iterate.for.lengthOf(3);
[2, 3, 5].should.iterate.for.length.above(2);
[2, 3, 5].should.iterate.for.length.below(4);
[2, 3, 5].should.iterate.for.length.of.at.least(3);
[2, 3, 5].should.iterate.for.length.of.at.most(3);
[2, 3, 5].should.iterate.for.length.within(2, 4);

[2, 3, 5].should.not.iterate.over([1, 2, 3]);
[{n: 2}, {n: 3}].should.deep.iterate.from([{n: 2}]);

Let's not limit ourselves to Arrays; we can test any iterable object.

 'abcde'.should.iterate.until(['c', 'd', 'e']);

And we can pass any iterable as our expected values too.

 'abcde'.should.iterate.until('cde');

User-defined iterable objects

chai-iterator is best used to test
user-defined iterable objects, like the one constructed by
the following class.

 class Count {

  constructor(start=0, step=1) {
    this.start = start;
    this.step = step;
  }

  *[Symbol.iterator]() {
    for (let n = this.start; true; n += this.step) {
      yield n;
    }
  }
}

The sequence generated by Count.prototype[@@iterator]() is infinite;
it continues to yield values indefinitely. Still, we can safely
use the from() assertion with it, since it will
terminate as soon as our expected iterable is done.

 let tens = new Count(10, 10);

tens.should.be.iterable;
tens.should.iterate.from([10, 20, 30]);
tens.should.iterate.from([10, 20, 30, 40, 50]);

Just don't go trying to use over() or
until() on infinite sequences. The former will always
fail and the latter will never stop.

Generators and iterators

Let's generate the fibonacci sequence. A
generator function is just a function that returns a
Generator object — an iterator that is also
iterable. We can test a Generator just as we would any other
iterable.

 function* fibonacci() {
  for (let [x, y] = [1, 1]; true; [x, y] = [y, x + y]) {
    yield x;
  }
}

fibonacci().should.iterate.from([1, 1, 2, 3, 5]);

Be careful though. Iterators can't go back in time. Once a value has
been yielded, it is lost forever. And so the following assertions pass.

 let fiborator = fibonacci();

fiborator.should.iterate.from([1, 1, 2, 3, 5]);
fiborator.should.iterate.from([8, 13, 21, 34]);

It usually makes more sense to construct a new Generator for each assertion.

 fibonacci().should.iterate.from([1, 1, 2, 3, 5]);
fibonacci().should.iterate.from([1, 1, 2, 3, 5, 8, 13]);

Compatibility

chai-iterator requires that Symbol.iterator be
available in the environment. In Node, this means the version must be
v4.0 or greater. While the latest versions of most browsers are
compatible, web-facing projects should almost certainly use a polyfill.

The Babel polyfill is one option for environments that do not
natively support Symbol.iterator. More minimally, we can get away with just
two sub-modules from the core-js library, like so.

 require('core-js/es6/symbol');
require('core-js/fn/symbol/iterator');

Installation

Install chai-iterator using npm. And be sure, of course, to install Chai.

 npm install --save chai chai-iterator

Setup

chai-iterator can be imported as a Node module, an AMD
module, or included in an HTML <script> tag. For
TypeScript users, declarations are installed with the package.

Node

To set up chai-iterator for Node, make sure the version is v4.0 or
higher, as prior versions lack support for the @@iterator
method.

 const chai = require('chai');
const chaiIterator = require('chai-iterator');

chai.use(chaiIterator);

AMD

chai-iterator can be set up inside of an AMD module like so.

 define((require, exports, module) => {
  let chai = require('chai');
  let chaiIterator = require('chai-iterator');

  chai.use(chaiIterator);
});

HTML script tag

chai-iterator can be included via a <script> tag. If it is
loaded after chai.js, Chai will use it automatically.

 <script src="chai.js"></script>
<script src="chai-iterator.js"></script>

TypeScript

TypeScript declarations are included in the package. To use them,
ensure chai-iterator is installed with npm, then install the declarations
and their dependencies via typings. And be sure to install the
declarations for chai.

 typings install --save-dev npm~chai npm:chai-iterator

In the compiler options, set "target" to "es6", or at
least include a reference to lib.es6.d.ts. Now the following will
just work.

 import chai = require("chai");
import chaiIterator = require("chai-iterator");

chai.use(chaiIterator);

[2, 3, 5].should.iterate.over([2, 3, 5]);

Expect/Should API

Assertions

iterable

Asserts that the target is an iterable object, i.e., that it has an
@@iterator method.

 expect([2, 3, 5]).to.be.iterable;
expect('abcdefg').to.be.iterable;
expect(12345).not.to.be.iterable;

iterate.over(expected)

Asserts that the target iterates over a given sequence of values. Set the
deep flag to use deep equality to compare values.

Param Type Description
expected object An iterable object.
 expect([2, 3, 5]).to.iterate.over([2, 3, 5]);
expect('abcdefg').to.itetate.over('abcdefg');
expect([2, 3, 5]).not.to.iterate.over([2, 3]);
expect([{n: 2}, {n: 3}]).to.deep.iterate.over([{n: 2}, {n: 3}]);

iterate.from(expected)

Asserts that the target begins iterating over a given sequence of values. Set
the deep flag to use deep equality to compare values.

Param Type Description
expected object An iterable object.
 expect([2, 3, 5]).to.iterate.from([2, 3]);
expect('abcdefg').to.iterate.from('abc');
expect([2, 3, 5]).not.to.iterate.from([3, 5]);
expect([{n: 2}, {n: 3}]).to.deep.iterate.from([{n: 2}]);

iterate.until(expected)

Asserts that the target ends iteration with a given sequence of values. Set the
deep flag to use deep equality to compare values.

Param Type Description
expected object An iterable object.
 expect([2, 3, 5]).to.iterate.until([3, 5]);
expect('abcdefg').to.iterate.until('efg');
expect([2, 3, 5]).not.to.iterate.until([2, 3]);
expect([{n: 2}, {n: 3}]).to.deep.iterate.until([{n: 3}]);

iterate.for.lengthOf(n)

Asserts that the target yields exactly n values.

Param Type Description
n number A positive integer
 expect([2, 3, 5]).to.iterate.for.lengthOf(3);
expect('abcdefg').to.iterate.for.lengthOf(7);
expect([2, 3, 5]).not.to.iterate.for.lengthOf(7);

iterate.for.length.above(n)

Asserts that the target yields more than n values.

Param Type Description
n number A positive integer
 expect([2, 3, 5]).to.iterate.for.length.above(2);
expect('abcdefg').to.iterate.for.length.above(5);
expect([2, 3, 5]).not.to.iterate.for.length.above(3);

iterate.for.length.below(n)

Asserts that the target yields fewer than n values.

Param Type Description
n number A positive integer
 expect([2, 3, 5]).to.iterate.for.length.below(4);
expect('abcdefg').to.iterate.for.length.below(10);
expect([2, 3, 5]).not.to.iterate.for.length.below(3);

iterate.for.length.of.at.least(n)

Asserts that the target yields at least n values.

Param Type Description
n number A positive integer
 expect([2, 3, 5]).to.iterate.for.length.of.at.least(2);
expect([2, 3, 5]).to.iterate.for.length.of.at.least(3);
expect([2, 3, 5]).not.to.iterate.for.length.of.at.least(4);

iterate.for.length.of.at.most(n)

Asserts that the target yields at most n values.

Param Type Description
n number A positive integer
 expect([2, 3, 5]).to.iterate.for.length.of.at.most(4);
expect([2, 3, 5]).to.iterate.for.length.of.at.most(3);
expect([2, 3, 5]).not.to.iterate.for.length.of.at.most(2);

iterate.for.length.within(min, max)

Asserts that the target yields between min and max values, inclusive.

Param Type Description
min number A positive integer
max number A positive integer
 expect([2, 3, 5]).to.iterate.for.length.within(2, 4);
expect([2, 3, 5]).to.iterate.for.length.within(3, 3);
expect([2, 3, 5]).not.to.iterate.for.length.within(4, 7);

Assert API

Assertions

Parameters

The parameters for the assert methods are as follows.

Param Type Description
value any Any value.
expected object An iterable object.
n number A positive integer.
message? string An optional message to display on error.

isIterable(value, [message])

Asserts that a value is an iterable object, i.e., that it is an object with
an @@iterator method.

 assert.isIterable([2, 3, 5]);
assert.isIterable('abcdefg');

isNotIterable(value, [message])

Asserts that a value is not an iterable object, i.e., that it lacks an
@@iterator method.

 assert.isNotIterable(235);
assert.isNotIterable(true);

iteratesOver(value, expected, [message])

Asserts that a value iterates exactly over a given sequence of values.

 assert.iteratesOver([2, 3, 5], [2, 3, 5]);
assert.iteratesOver('abcdefg', 'abcdefg');

doesNotIterateOver(value, expected, [message])

Asserts that a value does not iterate exactly over a given sequence of values.

 assert.doesNotIterateOver([2, 3, 5], [1, 2, 3]);
assert.doesNotIterateOver('abcdefg', 'abc');

deepIteratesOver(value, expected, [message])

Asserts that a value iterates exactly over a given sequence of values, using
deep equality.

 assert.deepIteratesOver([{n: 2}, {n: 3}], [{n: 2}, {n: 3}]);
assert.deepIteratesOver([[0, 2], [1, 3]], [[0, 2], [1, 3]]);

doesNotDeepIterateOver(value, expected, [message])

Asserts that a value does not iterate exactly over a given sequence of values,
using deep equality.

 assert.doesNotDeepIterateOver([{n: 2}, {n: 3}], [{n: 5}, {n: 7}]);
assert.doesNotDeepIterateOver([[0, 2], [1, 3]], [[1, 3], [0, 2]]);

iteratesFrom(value, expected, [message])

Asserts that a value begins iteration with a given sequence of values.

 assert.iteratesFrom([2, 3, 5], [2, 3, 5]);
assert.iteratesFrom([2, 3, 5], [2, 3]);
assert.iteratesFrom('abcdefg', 'abc');
assert.iteratesFrom('abcdefg', '');

doesNotIterateFrom(value, expected, [message])

Asserts that a value does not begin iteration with a given sequence of values.

 assert.doesNotIterateFrom([2, 3, 5], [3, 5]);
assert.doesNotIterateFrom('abcdefg', 'cdef');

deepIteratesFrom(value, expected, [message])

Asserts that a value begins iteration with a given sequence of values, using
deep equality.

 assert.deepIteratesFrom([{n: 2}, {n: 3}], [{n: 2}]);
assert.deepIteratesFrom([[0, 2], [1, 3]], [[0, 2]]);

doesNotDeepIterateFrom(value, expected, [message])

Asserts that a value does not begin iteration with a given sequence of values,
using deep equality.

 assert.doesNotDeepIterateFrom([{n: 2}, {n: 3}], [{n: 5}]);
assert.doesNotDeepIterateFrom([[0, 2], [1, 3]], [[1, 3]]);

iteratesUntil(value, expected, [message])

Asserts that a value ends iteration with a given sequence of values.

 assert.iteratesUntil([2, 3, 5], [2, 3, 5]);
assert.iteratesUntil([2, 3, 5], [3, 5]);
assert.iteratesUntil('abcdefg', 'efg');
assert.iteratesUntil('abcdefg', '');

doesNotIterateUntil(value, expected, [message])

Asserts that a value does not end iteration with a given sequence of values.

 assert.doesNotIterateUntil([2, 3, 5], [2, 3]);
assert.doesNotIterateUntil('abcdefg', 'cdef');

deepIteratesUntil(value, expected, [message])

Asserts that a value ends iteration with a given sequence of values, using
deep equality.

 assert.deepIteratesUntil([{n: 2}, {n: 3}], [{n: 3}]);
assert.deepIteratesUntil([[0, 2], [1, 3]], [[1, 3]]);

doesNotDeepIterateUntil(value, expected, [message])

Asserts that a value does not end iteration with a given sequence of values,
using deep equality.

 assert.doesNotDeepIterateUntil([{n: 2}, {n: 3}], [{n: 5}]);
assert.doesNotDeepIterateUntil([[0, 2], [1, 3]], [[0, 2]]);

lengthOf(value, n, [message])

Asserts that an iterable yields a given number of values. If value is not an
iterable object, or if it has a 'length' property, Chai's built-in
assert.lengthOf() will be used.

 function* range(min=0, max=Infinity, step=1) {
  for (let n = min; n < max; n += step) {
    yield n;
  }
}

assert.lengthOf(range(0, 10), 10);
assert.lengthOf(range(6, 42), 36);

License

Copyright © 2016–2017 Akim McMath. Licensed under the [MIT License][license].

RELATED POST

10 Must-Know Windows Shortcuts That Will Save You Time

10 Must-Know Windows Shortcuts That Will Save You Time

Arrays vs Linked Lists: Which is Better for Memory Management in Data Structures?

Arrays vs Linked Lists: Which is Better for Memory Management in Data Structures?

Navigating AWS Networking: Essential Hacks for Smooth Operation

Navigating AWS Networking: Essential Hacks for Smooth Operation

Achieving Stunning Visuals with Unity's Global Illumination

Achieving Stunning Visuals with Unity's Global Illumination

Nim's Hidden Gems: Lesser-known Features for Writing Efficient Code

Nim's Hidden Gems: Lesser-known Features for Writing Efficient Code