X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/d741139aff475642fe08978d39722b298cf96802..bf3fe705a9d5f717b3c1794a12726e926ece7ecc:/iterable.test.js diff --git a/iterable.test.js b/iterable.test.js new file mode 100644 index 0000000..2e62d5c --- /dev/null +++ b/iterable.test.js @@ -0,0 +1,530 @@ +// ♓🌟 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 . + +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(); + }); + }); + }); + }); +});