X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/7c3a2eda590637af9463e5a59ab798f802d274a9..29307967cb084aceaa9265fe22af4348c9e4376a:/function.test.js diff --git a/function.test.js b/function.test.js new file mode 100644 index 0000000..4cfa85f --- /dev/null +++ b/function.test.js @@ -0,0 +1,292 @@ +// ♓🌟 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 . + +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 {}(), + ), + ); + }); +});