--- /dev/null
+// ♓🌟 Piscēs ∷ value.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,
+ assertStrictEquals,
+ assertThrows,
+ describe,
+ it,
+} from "./dev-deps.js";
+import {
+ NULL,
+ ordinaryToPrimitive,
+ sameValue,
+ sameValueZero,
+ toPrimitive,
+ type,
+ UNDEFINED,
+} from "./value.js";
+
+describe("NULL", () => {
+ it("[[Get]] is null", () => {
+ assertStrictEquals(NULL, null);
+ });
+});
+
+describe("UNDEFINED", () => {
+ it("[[Get]] is undefined", () => {
+ assertStrictEquals(UNDEFINED, void {});
+ });
+});
+
+describe("ordinaryToPrimitive", () => {
+ it("[[Call]] prefers `valueOf` by default", () => {
+ const obj = {
+ toString() {
+ return "failure";
+ },
+ valueOf() {
+ return "success";
+ },
+ };
+ assertStrictEquals(ordinaryToPrimitive(obj), "success");
+ assertStrictEquals(ordinaryToPrimitive(obj, "default"), "success");
+ });
+
+ it('[[Call]] prefers `valueOf` for a "number" hint', () => {
+ const obj = {
+ toString() {
+ return "failure";
+ },
+ valueOf() {
+ return "success";
+ },
+ };
+ assertStrictEquals(ordinaryToPrimitive(obj, "number"), "success");
+ });
+
+ it('[[Call]] prefers `toString` for a "string" hint', () => {
+ const obj = {
+ toString() {
+ return "success";
+ },
+ valueOf() {
+ return "failure";
+ },
+ };
+ assertStrictEquals(ordinaryToPrimitive(obj, "string"), "success");
+ });
+
+ it("[[Call]] falls back to the other method if the first isn’t callable", () => {
+ const obj = {
+ toString() {
+ return "success";
+ },
+ valueOf: "failure",
+ };
+ assertStrictEquals(ordinaryToPrimitive(obj), "success");
+ });
+
+ it("[[Call]] falls back to the other method if the first returns an object", () => {
+ const obj = {
+ toString() {
+ return "success";
+ },
+ valueOf() {
+ return new String("failure");
+ },
+ };
+ assertStrictEquals(ordinaryToPrimitive(obj), "success");
+ });
+
+ it("[[Call]] throws an error if neither method is callable", () => {
+ const obj = {
+ toString: "failure",
+ valueOf: "failure",
+ };
+ assertThrows(() => ordinaryToPrimitive(obj));
+ });
+
+ it("[[Call]] throws an error if neither method returns an object", () => {
+ const obj = {
+ toString() {
+ return new String("failure");
+ },
+ valueOf() {
+ return new String("failure");
+ },
+ };
+ assertThrows(() => ordinaryToPrimitive(obj));
+ });
+});
+
+describe("sameValue", () => {
+ it("[[Call]] returns false for null 🆚 undefined", () => {
+ assert(!sameValue(null, void {}));
+ });
+
+ it("[[Call]] returns false for null 🆚 an object", () => {
+ assert(!sameValue(null, {}));
+ });
+
+ it("[[Call]] returns true for null 🆚 null", () => {
+ assert(sameValue(null, null));
+ });
+
+ it("[[Call]] returns false for two different objects", () => {
+ assert(!sameValue({}, {}));
+ });
+
+ it("[[Call]] returns true for the same object", () => {
+ const obj = {};
+ assert(sameValue(obj, obj));
+ });
+
+ it("[[Call]] returns false for ±0", () => {
+ assert(!sameValue(0, -0));
+ });
+
+ it("[[Call]] returns true for -0", () => {
+ assert(sameValue(-0, -0));
+ });
+
+ it("[[Call]] returns true for nan", () => {
+ assert(sameValue(0 / 0, 0 / 0));
+ });
+
+ it("[[Call]] returns false for a primitive and its wrapped object", () => {
+ assert(!sameValue(false, new Boolean(false)));
+ });
+});
+
+describe("sameValueZero", () => {
+ it("[[Call]] returns false for null 🆚 undefined", () => {
+ assert(!sameValueZero(null, void {}));
+ });
+
+ it("[[Call]] returns false for null 🆚 an object", () => {
+ assert(!sameValueZero(null, {}));
+ });
+
+ it("[[Call]] returns true for null 🆚 null", () => {
+ assert(sameValueZero(null, null));
+ });
+
+ it("[[Call]] returns false for two different objects", () => {
+ assert(!sameValueZero({}, {}));
+ });
+
+ it("[[Call]] returns true for the same object", () => {
+ const obj = {};
+ assert(sameValueZero(obj, obj));
+ });
+
+ it("[[Call]] returns true for ±0", () => {
+ assert(sameValueZero(0, -0));
+ });
+
+ it("[[Call]] returns true for -0", () => {
+ assert(sameValueZero(-0, -0));
+ });
+
+ it("[[Call]] returns true for nan", () => {
+ assert(sameValueZero(0 / 0, 0 / 0));
+ });
+
+ it("[[Call]] returns false for a primitive and its wrapped object", () => {
+ assert(!sameValueZero(false, new Boolean(false)));
+ });
+});
+
+describe("toPrimitive", () => {
+ it("[[Call]] returns the argument when passed a primitive", () => {
+ const value = Symbol();
+ assertStrictEquals(toPrimitive(value), value);
+ });
+
+ it("[[Call]] works with nullish values", () => {
+ assertStrictEquals(toPrimitive(null), null);
+ assertStrictEquals(toPrimitive(), void {});
+ });
+
+ it("[[Call]] calls ordinaryToPrimitive by default", () => {
+ const value = Object.assign(
+ Object.create(null),
+ {
+ valueOf() {
+ return "success";
+ },
+ },
+ );
+ assertStrictEquals(toPrimitive(value), "success");
+ });
+
+ it("[[Call]] accepts a hint", () => {
+ const value = Object.assign(
+ Object.create(null),
+ {
+ toString() {
+ return "success";
+ },
+ valueOf() {
+ return "failure";
+ },
+ },
+ );
+ assertStrictEquals(toPrimitive(value, "string"), "success");
+ });
+
+ it("[[Call]] uses the exotic toPrimitive method if available", () => {
+ const value = Object.assign(
+ Object.create(null),
+ {
+ [Symbol.toPrimitive]() {
+ return "success";
+ },
+ },
+ );
+ assertStrictEquals(toPrimitive(value), "success");
+ });
+
+ it("[[Call]] passes the hint to the exotic toPrimitive", () => {
+ const value = Object.assign(
+ Object.create(null),
+ {
+ [Symbol.toPrimitive](hint) {
+ return hint === "string" ? "success" : "failure";
+ },
+ },
+ );
+ assertStrictEquals(toPrimitive(value, "string"), "success");
+ });
+
+ it('[[Call]] passes a "default" hint by default', () => {
+ const value = Object.assign(
+ Object.create(null),
+ {
+ [Symbol.toPrimitive](hint) {
+ return hint === "default" ? "success" : "failure";
+ },
+ },
+ );
+ assertStrictEquals(toPrimitive(value, "default"), "success");
+ });
+
+ it("[[Call]] throws for an invalid hint", () => {
+ const value1 = Object.assign(
+ Object.create(null),
+ {
+ [Symbol.toPrimitive]() {
+ return "success";
+ },
+ },
+ );
+ const value2 = Object.assign(
+ Object.create(null),
+ {
+ valueOf() {
+ return true;
+ },
+ },
+ );
+ assertThrows(() => toPrimitive(value1, "badhint"));
+ assertThrows(() => toPrimitive(value2, "badhint"));
+ assertThrows(() => toPrimitive(true, "badhint"));
+ });
+});
+
+describe("type", () => {
+ it('[[Call]] returns "null" for null', () => {
+ assertStrictEquals(type(null), "null");
+ });
+
+ it('[[Call]] returns "undefined" for undefined', () => {
+ assertStrictEquals(type(void {}), "undefined");
+ });
+
+ it('[[Call]] returns "object" for non‐callable objects', () => {
+ assertStrictEquals(type(Object.create(null)), "object");
+ });
+
+ it('[[Call]] returns "object" for callable objects', () => {
+ assertStrictEquals(type(() => {}), "object");
+ });
+
+ it('[[Call]] returns "object" for constructable objects', () => {
+ assertStrictEquals(type(class {}), "object");
+ });
+});