+// ♓🌟 Piscēs ∷ function.test.js
+// ====================================================================
+//
+// Copyright © 2022 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 {
+ assert,
+ assertEquals,
+ assertStrictEquals,
+ describe,
+ it,
+} from "./dev-deps.js";
+import {
+ bind,
+ call,
+ construct,
+ identity,
+ isCallable,
+ isConstructor,
+ makeCallable,
+ ordinaryHasInstance,
+} from "./function.js";
+
+describe("bind", () => {
+ it("[[Call]] binds this", () => {
+ assertStrictEquals(
+ bind(
+ function () {
+ return this;
+ },
+ "pass",
+ [],
+ ).call("fail"),
+ "pass",
+ );
+ });
+
+ it("[[Call]] binds arguments", () => {
+ assertEquals(
+ bind(
+ function (...args) {
+ return [this, ...args];
+ },
+ "etaoin",
+ ["shrdlu"],
+ ).call("failure", "cmfwyp"),
+ ["etaoin", "shrdlu", "cmfwyp"],
+ );
+ });
+
+ it("[[Call]] works with any arraylike third argument", () => {
+ assertEquals(
+ bind(
+ function (...args) {
+ return [this, ...args];
+ },
+ "etaoin",
+ {
+ 1: "shrdlu",
+ 3: "failure",
+ length: 3,
+ },
+ ).call("failure", "cmfwyp"),
+ ["etaoin", undefined, "shrdlu", undefined, "cmfwyp"],
+ );
+ });
+});
+
+describe("call", () => {
+ it("[[Call]] calls with the provided this value", () => {
+ assertStrictEquals(
+ call(
+ function () {
+ return this;
+ },
+ "pass",
+ [],
+ ),
+ "pass",
+ );
+ });
+
+ it("[[Call]] calls with the provided arguments", () => {
+ assertEquals(
+ call(
+ function (...args) {
+ return [this, ...args];
+ },
+ "etaoin",
+ ["shrdlu", "cmfwyp"],
+ ),
+ ["etaoin", "shrdlu", "cmfwyp"],
+ );
+ });
+
+ it("[[Call]] works with any arraylike third argument", () => {
+ assertEquals(
+ call(
+ function (...args) {
+ return [this, ...args];
+ },
+ "etaoin",
+ {
+ 1: "shrdlu",
+ 3: "cmfwyp",
+ 4: "failure",
+ length: 4,
+ },
+ ),
+ ["etaoin", undefined, "shrdlu", undefined, "cmfwyp"],
+ );
+ });
+});
+
+describe("construct", () => {
+ it("[[Call]] defaults to the constructor as the target", () => {
+ const constructor = class {};
+ assertStrictEquals(
+ Object.getPrototypeOf(construct(
+ constructor,
+ [],
+ )),
+ constructor.prototype,
+ );
+ });
+
+ it("[[Call]] constructs with the provided new target", () => {
+ const target = function () {};
+ assertStrictEquals(
+ construct(
+ function () {
+ return new.target;
+ },
+ [],
+ target,
+ ),
+ target,
+ );
+ });
+
+ it("[[Call]] constructs with the provided arguments", () => {
+ assertEquals(
+ construct(
+ function (...args) {
+ return [new.target.value, ...args];
+ },
+ ["shrdlu", "cmfwyp"],
+ Object.assign(function () {}, { value: "etaoin" }),
+ ),
+ ["etaoin", "shrdlu", "cmfwyp"],
+ );
+ });
+
+ it("[[Call]] works with any arraylike third argument", () => {
+ assertEquals(
+ construct(
+ function (...args) {
+ return [new.target.value, ...args];
+ },
+ {
+ 1: "shrdlu",
+ 3: "cmfwyp",
+ 4: "failure",
+ length: 4,
+ },
+ Object.assign(function () {}, { value: "etaoin" }),
+ ),
+ ["etaoin", undefined, "shrdlu", undefined, "cmfwyp"],
+ );
+ });
+});
+
+describe("identity", () => {
+ it("[[Call]] returns what it is given", () => {
+ const value = {};
+ assertStrictEquals(identity(value), value);
+ });
+
+ it("[[Construct]] is constructable", () => {
+ const value = {};
+ assertStrictEquals(new identity(value), value);
+ });
+
+ it("[[Construct]] is subclassable", () => {
+ const value = {};
+ assertStrictEquals(new class extends identity {}(value), value);
+ });
+});
+
+describe("isCallable", () => {
+ it("[[Call]] returns true for ordinary functions", () => {
+ assert(isCallable(function () {}));
+ });
+
+ it("[[Call]] returns true for arrow functions", () => {
+ assert(isCallable(() => {}));
+ });
+
+ it("[[Call]] returns true for generator functions", () => {
+ assert(isCallable(function* () {}));
+ });
+
+ it("[[Call]] returns true for classes", () => {
+ assert(isCallable(class {}));
+ });
+
+ it("[[Call]] returns true for builtin functions", () => {
+ assert(isCallable(Math.ceil));
+ });
+
+ it("[[Call]] returns false for null", () => {
+ assert(!isCallable(null));
+ });
+
+ it("[[Call]] returns false for objects", () => {
+ assert(!isCallable({}));
+ });
+});
+
+describe("isConstructor", () => {
+ it("[[Call]] returns true for ordinary functions", () => {
+ assert(isConstructor(function () {}));
+ });
+
+ it("[[Call]] returns false for arrow functions", () => {
+ assert(!isConstructor(() => {}));
+ });
+
+ it("[[Call]] returns false for generator functions", () => {
+ assert(!isConstructor(function* () {}));
+ });
+
+ it("[[Call]] returns true for classes", () => {
+ assert(isConstructor(class {}));
+ });
+
+ it("[[Call]] returns false for builtin functions", () => {
+ assert(!isConstructor(Math.ceil));
+ });
+
+ it("[[Call]] returns false for null", () => {
+ assert(!isConstructor(null));
+ });
+
+ it("[[Call]] returns false for objects", () => {
+ assert(!isConstructor({}));
+ });
+});
+
+describe("makeCallable", () => {
+ it("[[Call]] transfers the first argument to this", () => {
+ assertStrictEquals(
+ makeCallable(
+ function () {
+ return this;
+ },
+ ).call("fail", "pass"),
+ "pass",
+ );
+ });
+
+ it("[[Call]] transfers the remaining arguments", () => {
+ assertEquals(
+ makeCallable(
+ function (...args) {
+ return [this, ...args];
+ },
+ ).call("failure", "etaoin", "shrdlu", "cmfwyp"),
+ ["etaoin", "shrdlu", "cmfwyp"],
+ );
+ });
+});
+
+describe("ordinaryHasInstance", () => {
+ it("[[Call]] walks the prototype chain", () => {
+ const constructor = class {
+ [Symbol.hasInstance]() {
+ return false;
+ }
+ };
+ assert(
+ ordinaryHasInstance(
+ constructor,
+ new class extends constructor {}(),
+ ),
+ );
+ });
+});