+// ♓🌟 Piscēs ∷ iterable.test.js
+// ====================================================================
+//
+// Copyright © 2023 Lady [@ Lady’s Computer].
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+
+import {
+ assertEquals,
+ assertStrictEquals,
+ assertThrows,
+ describe,
+ it,
+} from "./dev-deps.js";
+import {
+ arrayIteratorFunction,
+ generatorIteratorFunction,
+ mapIteratorFunction,
+ setIteratorFunction,
+ stringIteratorFunction,
+} from "./iterable.js";
+
+describe("arrayIteratorFunction", () => {
+ it("[[Call]] returns a function", () => {
+ assertStrictEquals(typeof arrayIteratorFunction(), "function");
+ });
+
+ it("[[Call]] returns a value which has a prototype of %FunctionPrototype%", () => {
+ assertStrictEquals(
+ Object.getPrototypeOf(arrayIteratorFunction()),
+ Function.prototype,
+ );
+ });
+
+ describe("()", () => {
+ it("[[Call]] returns a value which inherits from %IteratorPrototype%", () => {
+ const iteratorProto = Object.getPrototypeOf(
+ Object.getPrototypeOf([][Symbol.iterator]),
+ );
+ const iterator = arrayIteratorFunction();
+ assertStrictEquals(
+ iterator([]) instanceof Object.assign(
+ function () {},
+ { prototype: iteratorProto },
+ ),
+ true,
+ );
+ });
+
+ it("[[Call]] returns a value with the provided string tag", () => {
+ const iterator = arrayIteratorFunction(null, "My Iterator");
+ assertStrictEquals(
+ iterator([])[Symbol.toStringTag],
+ "My Iterator",
+ );
+ });
+
+ it("[[Call]] yields the values", () => {
+ const iterator = arrayIteratorFunction();
+ assertEquals(
+ [...iterator(["etaoin", "shrdlu"])],
+ ["etaoin", "shrdlu"],
+ );
+ });
+
+ it("[[Call]] maps the values", () => {
+ const iterator = arrayIteratorFunction(function* ($) {
+ yield $.toUpperCase();
+ });
+ assertEquals(
+ [...iterator(["etaoin", "shrdlu"])],
+ ["ETAOIN", "SHRDLU"],
+ );
+ });
+
+ it("[[Call]] can map to nothing", () => {
+ const iterator = arrayIteratorFunction(function* () {});
+ assertEquals(
+ [...iterator(["etaoin", "shrdlu"])],
+ [],
+ );
+ });
+
+ it("[[Call]] can map to multiple values", () => {
+ const iterator = arrayIteratorFunction(function* ($) {
+ yield* $;
+ });
+ assertEquals(
+ [...iterator(["etaoin", "shrdlu"])],
+ [..."etaoinshrdlu"],
+ );
+ });
+
+ it("[[Call]] throws if not provided with any arguments", () => {
+ const iterator = arrayIteratorFunction();
+ assertThrows(() => {
+ iterator();
+ });
+ });
+
+ it("[[Call]] throws if not provided an arraylike", () => {
+ const iterator = arrayIteratorFunction();
+ assertThrows(() => {
+ iterator(null);
+ });
+ });
+
+ describe("::next", () => {
+ it("[[Call]] throws if there are values and the mapper is not a generator function", () => {
+ const iterator = arrayIteratorFunction(function () {});
+ assertThrows(() => {
+ iterator(["etaoin"]).next();
+ });
+ });
+ });
+ });
+});
+
+describe("generatorIteratorFunction", () => {
+ it("[[Call]] returns a function", () => {
+ assertStrictEquals(typeof generatorIteratorFunction(), "function");
+ });
+
+ it("[[Call]] returns a value which has a prototype of %FunctionPrototype%", () => {
+ assertStrictEquals(
+ Object.getPrototypeOf(generatorIteratorFunction()),
+ Function.prototype,
+ );
+ });
+
+ describe("()", () => {
+ it("[[Call]] returns a value which inherits from %IteratorPrototype%", () => {
+ const iteratorProto = Object.getPrototypeOf(
+ Object.getPrototypeOf([][Symbol.iterator]),
+ );
+ const iterator = generatorIteratorFunction();
+ assertStrictEquals(
+ iterator(function* () {}) instanceof Object.assign(
+ function () {},
+ { prototype: iteratorProto },
+ ),
+ true,
+ );
+ });
+
+ it("[[Call]] returns a value with the provided string tag", () => {
+ const iterator = generatorIteratorFunction(null, "My Iterator");
+ assertStrictEquals(
+ iterator(function* () {})[Symbol.toStringTag],
+ "My Iterator",
+ );
+ });
+
+ it("[[Call]] yields the values", () => {
+ const generator = function* () {
+ yield* ["etaoin", "shrdlu"];
+ };
+ const iterator = generatorIteratorFunction();
+ assertEquals(
+ [...iterator(generator)],
+ ["etaoin", "shrdlu"],
+ );
+ });
+
+ it("[[Call]] maps the values", () => {
+ const generator = function* () {
+ yield* ["etaoin", "shrdlu"];
+ };
+ const iterator = generatorIteratorFunction(function* ($) {
+ yield $.toUpperCase();
+ });
+ assertEquals(
+ [...iterator(generator)],
+ ["ETAOIN", "SHRDLU"],
+ );
+ });
+
+ it("[[Call]] can map to nothing", () => {
+ const generator = function* () {
+ yield* ["etaoin", "shrdlu"];
+ };
+ const iterator = generatorIteratorFunction(function* () {});
+ assertEquals(
+ [...iterator(generator)],
+ [],
+ );
+ });
+
+ it("[[Call]] can map to multiple values", () => {
+ const generator = function* () {
+ yield* ["etaoin", "shrdlu"];
+ };
+ const iterator = generatorIteratorFunction(function* ($) {
+ yield* $;
+ });
+ assertEquals(
+ [...iterator(generator)],
+ [..."etaoinshrdlu"],
+ );
+ });
+
+ it("[[Call]] throws if not provided with any arguments", () => {
+ const iterator = generatorIteratorFunction();
+ assertThrows(() => {
+ iterator();
+ });
+ });
+
+ it("[[Call]] throws if not provided a function", () => {
+ const iterator = generatorIteratorFunction();
+ assertThrows(() => {
+ iterator([]);
+ });
+ });
+
+ describe("::next", () => {
+ it("[[Call]] throws if there are values and the mapper is not a generator function", () => {
+ const generator = function* () {
+ yield "etaoin";
+ };
+ const iterator = generatorIteratorFunction(function () {});
+ assertThrows(() => {
+ iterator(generator).next();
+ });
+ });
+
+ it("[[Call]] throws if not constructed with a generator function", () => {
+ const iterator = generatorIteratorFunction();
+ assertThrows(() => {
+ iterator(Array.prototype[Symbol.iterator].bind([])).next();
+ });
+ });
+ });
+ });
+});
+
+describe("mapIteratorFunction", () => {
+ it("[[Call]] returns a function", () => {
+ assertStrictEquals(typeof mapIteratorFunction(), "function");
+ });
+
+ it("[[Call]] returns a value which has a prototype of %FunctionPrototype%", () => {
+ assertStrictEquals(
+ Object.getPrototypeOf(mapIteratorFunction()),
+ Function.prototype,
+ );
+ });
+
+ describe("()", () => {
+ it("[[Call]] returns a value which inherits from %IteratorPrototype%", () => {
+ const iteratorProto = Object.getPrototypeOf(
+ Object.getPrototypeOf([][Symbol.iterator]),
+ );
+ const iterator = mapIteratorFunction();
+ assertStrictEquals(
+ iterator(new Map()) instanceof Object.assign(
+ function () {},
+ { prototype: iteratorProto },
+ ),
+ true,
+ );
+ });
+
+ it("[[Call]] returns a value with the provided string tag", () => {
+ const iterator = mapIteratorFunction(null, "My Iterator");
+ assertStrictEquals(
+ iterator(new Map())[Symbol.toStringTag],
+ "My Iterator",
+ );
+ });
+
+ it("[[Call]] yields the values", () => {
+ const iterator = mapIteratorFunction();
+ assertEquals(
+ [...iterator(new Map([["etaoin", "shrdlu"]]))],
+ [["etaoin", "shrdlu"]],
+ );
+ });
+
+ it("[[Call]] maps the values", () => {
+ const iterator = mapIteratorFunction(function* ([k, v]) {
+ yield [k.toUpperCase(), v.toUpperCase()];
+ });
+ assertEquals(
+ [...iterator(new Map([["etaoin", "shrdlu"]]))],
+ [["ETAOIN", "SHRDLU"]],
+ );
+ });
+
+ it("[[Call]] can map to nothing", () => {
+ const iterator = mapIteratorFunction(function* () {});
+ assertEquals(
+ [...iterator(new Map([["etaoin", "shrdlu"]]))],
+ [],
+ );
+ });
+
+ it("[[Call]] can map to multiple values", () => {
+ const iterator = mapIteratorFunction(function* ($) {
+ yield* $;
+ });
+ assertEquals(
+ [...iterator(new Map([["etaoin", "shrdlu"]]))],
+ ["etaoin", "shrdlu"],
+ );
+ });
+
+ it("[[Call]] throws if not provided with any arguments", () => {
+ const iterator = mapIteratorFunction();
+ assertThrows(() => {
+ iterator();
+ });
+ });
+
+ it("[[Call]] throws if not provided a map", () => {
+ const iterator = mapIteratorFunction();
+ assertThrows(() => {
+ iterator([]);
+ });
+ });
+
+ describe("::next", () => {
+ it("[[Call]] throws if there are values and the mapper is not a generator function", () => {
+ const iterator = mapIteratorFunction(function () {});
+ assertThrows(() => {
+ iterator(new Map([["etaoin", "shrdlu"]])).next();
+ });
+ });
+ });
+ });
+});
+
+describe("setIteratorFunction", () => {
+ it("[[Call]] returns a function", () => {
+ assertStrictEquals(typeof setIteratorFunction(), "function");
+ });
+
+ it("[[Call]] returns a value which has a prototype of %FunctionPrototype%", () => {
+ assertStrictEquals(
+ Object.getPrototypeOf(setIteratorFunction()),
+ Function.prototype,
+ );
+ });
+
+ describe("()", () => {
+ it("[[Call]] returns a value which inherits from %IteratorPrototype%", () => {
+ const iteratorProto = Object.getPrototypeOf(
+ Object.getPrototypeOf([][Symbol.iterator]),
+ );
+ const iterator = setIteratorFunction();
+ assertStrictEquals(
+ iterator(new Set()) instanceof Object.assign(
+ function () {},
+ { prototype: iteratorProto },
+ ),
+ true,
+ );
+ });
+
+ it("[[Call]] returns a value with the provided string tag", () => {
+ const iterator = setIteratorFunction(null, "My Iterator");
+ assertStrictEquals(
+ iterator(new Set())[Symbol.toStringTag],
+ "My Iterator",
+ );
+ });
+
+ it("[[Call]] yields the values", () => {
+ const iterator = setIteratorFunction();
+ assertEquals(
+ [...iterator(new Set(["etaoin", "shrdlu"]))],
+ ["etaoin", "shrdlu"],
+ );
+ });
+
+ it("[[Call]] maps the values", () => {
+ const iterator = setIteratorFunction(function* ($) {
+ yield $.toUpperCase();
+ });
+ assertEquals(
+ [...iterator(new Set(["etaoin", "shrdlu"]))],
+ ["ETAOIN", "SHRDLU"],
+ );
+ });
+
+ it("[[Call]] can map to nothing", () => {
+ const iterator = setIteratorFunction(function* () {});
+ assertEquals(
+ [...iterator(new Set(["etaoin", "shrdlu"]))],
+ [],
+ );
+ });
+
+ it("[[Call]] can map to multiple values", () => {
+ const iterator = setIteratorFunction(function* ($) {
+ yield* $;
+ });
+ assertEquals(
+ [...iterator(new Set(["etaoin", "shrdlu"]))],
+ [..."etaoinshrdlu"],
+ );
+ });
+
+ it("[[Call]] throws if not provided with any arguments", () => {
+ const iterator = setIteratorFunction();
+ assertThrows(() => {
+ iterator();
+ });
+ });
+
+ it("[[Call]] throws if not provided a set", () => {
+ const iterator = setIteratorFunction();
+ assertThrows(() => {
+ iterator([]);
+ });
+ });
+
+ describe("::next", () => {
+ it("[[Call]] throws if there are values and the mapper is not a generator function", () => {
+ const iterator = setIteratorFunction(function () {});
+ assertThrows(() => {
+ iterator(new Set(["etaoin"])).next();
+ });
+ });
+ });
+ });
+});
+
+describe("stringIteratorFunction", () => {
+ it("[[Call]] returns a function", () => {
+ assertStrictEquals(typeof stringIteratorFunction(), "function");
+ });
+
+ it("[[Call]] returns a value which has a prototype of %FunctionPrototype%", () => {
+ assertStrictEquals(
+ Object.getPrototypeOf(stringIteratorFunction()),
+ Function.prototype,
+ );
+ });
+
+ describe("()", () => {
+ it("[[Call]] returns a value which inherits from %IteratorPrototype%", () => {
+ const iteratorProto = Object.getPrototypeOf(
+ Object.getPrototypeOf([][Symbol.iterator]),
+ );
+ const iterator = stringIteratorFunction();
+ assertStrictEquals(
+ iterator("") instanceof Object.assign(
+ function () {},
+ { prototype: iteratorProto },
+ ),
+ true,
+ );
+ });
+
+ it("[[Call]] returns a value with the provided string tag", () => {
+ const iterator = stringIteratorFunction(null, "My Iterator");
+ assertStrictEquals(
+ iterator("")[Symbol.toStringTag],
+ "My Iterator",
+ );
+ });
+
+ it("[[Call]] yields the values", () => {
+ const iterator = stringIteratorFunction();
+ assertEquals(
+ [...iterator("etaoin👀")],
+ [..."etaoin👀"],
+ );
+ });
+
+ it("[[Call]] maps the values", () => {
+ const iterator = stringIteratorFunction(function* ($) {
+ yield $.toUpperCase();
+ });
+ assertEquals(
+ [...iterator("etaoin👀")],
+ [..."ETAOIN👀"],
+ );
+ });
+
+ it("[[Call]] can map to nothing", () => {
+ const iterator = stringIteratorFunction(function* () {});
+ assertEquals(
+ [...iterator("etaoin👀")],
+ [],
+ );
+ });
+
+ it("[[Call]] can map to multiple values", () => {
+ const iterator = stringIteratorFunction(function* ($) {
+ yield $;
+ yield $;
+ });
+ assertEquals(
+ [...iterator("etaoin👀")],
+ [..."eettaaooiinn👀👀"],
+ );
+ });
+
+ it("[[Call]] throws if not provided with any arguments", () => {
+ const iterator = stringIteratorFunction();
+ assertThrows(() => {
+ iterator();
+ });
+ });
+
+ it("[[Call]] throws if not provided something convertible to a string", () => {
+ const iterator = stringIteratorFunction();
+ assertThrows(() => {
+ iterator({
+ toString() {
+ throw null;
+ },
+ });
+ });
+ });
+
+ describe("::next", () => {
+ it("[[Call]] throws if there are values and the mapper is not a generator function", () => {
+ const iterator = stringIteratorFunction(function () {});
+ assertThrows(() => {
+ iterator("etaoin").next();
+ });
+ });
+ });
+ });
+});