]> Lady’s Gitweb - Pisces/commitdiff
Test constructability, name, length in string.js
authorLady <redacted>
Sat, 11 Nov 2023 02:55:58 +0000 (21:55 -0500)
committerLady <redacted>
Sat, 11 Nov 2023 02:55:58 +0000 (21:55 -0500)
string.js
string.test.js

index edf18810ee241a20cfede1c6826480ce81e34c20..774261dbdf90f8a8e49e38e51f4e61069d318a6a 100644 (file)
--- a/string.js
+++ b/string.js
@@ -10,6 +10,7 @@
 import {
   bind,
   call,
+  createArrowFunction,
   createCallableFunction,
   identity,
 } from "./function.js";
@@ -302,6 +303,13 @@ export const {
 })();
 
 export const {
+  /**
+   * Returns an iterator over the codepoints in the string representation
+   * of the provided value according to the algorithm of
+   * `String::[Symbol.iterator]`.
+   */
+  characters,
+
   /**
    * Returns an iterator over the code units in the string
    * representation of the provided value.
@@ -330,6 +338,9 @@ export const {
    */
   scalarValueString,
 } = (() => {
+  const generateCharacters = function* (character) {
+    yield character;
+  };
   const generateCodeUnits = function* (ucsCharacter) {
     yield getCodeUnit(ucsCharacter, 0);
   };
@@ -341,6 +352,10 @@ export const {
       : 0xFFFD;
   };
 
+  const charactersIterator = stringIteratorFunction(
+    generateCharacters,
+    "String Character Iterator",
+  );
   const codeUnitsIterator = arrayIteratorFunction(
     generateCodeUnits,
     "String Code Unit Iterator",
@@ -369,6 +384,7 @@ export const {
   };
 
   return {
+    characters: ($) => charactersIterator(`${$}`),
     codeUnits: ($) => codeUnitsIterator(`${$}`),
     codepoints: ($) => codepointsIterator(`${$}`),
     scalarValues: ($) => scalarValuesIterator(`${$}`),
@@ -380,16 +396,6 @@ export const {
   };
 })();
 
-/**
- * Returns an iterator over the codepoints in the string representation
- * of the provided value according to the algorithm of
- * `String::[Symbol.iterator]`.
- */
-export const characters = createCallableFunction(
-  stringPrototype[ITERATOR],
-  "characters",
-);
-
 /**
  * Returns the character at the provided position in the string
  * representation of the provided value according to the algorithm of
@@ -405,12 +411,38 @@ export const getCharacter = ($, pos) => {
 /**
  * Returns the code unit at the provided position in the string
  * representation of the provided value according to the algorithm of
- * `String::charAt`.
+ * `String::charAt`, except that out‐of‐bounds values return undefined
+ * in place of nan.
  */
-export const getCodeUnit = createCallableFunction(
-  stringPrototype.charCodeAt,
-  "getCodeUnit",
-);
+export const {
+  getCodeUnit,
+
+  /**
+   * Returns the result of catenating the string representations of the
+   * provided values, returning a new string according to the algorithm
+   * of `String::concat`.
+   *
+   * ※ If no arguments are given, this function returns the empty
+   * string. This is different behaviour than if an explicit undefined
+   * first argument is given, in which case the resulting string will
+   * begin with `"undefined"`.
+   */
+  stringCatenate,
+} = (() => {
+  const { charCodeAt, concat } = String.prototype;
+  const { isNaN: isNan } = Number;
+
+  return {
+    getCodeUnit: ($, n) => {
+      const codeUnit = call(charCodeAt, $, [n]);
+      return isNan(codeUnit) ? undefined : codeUnit;
+    },
+    stringCatenate: defineOwnProperties(
+      (...args) => call(concat, "", args),
+      { name: { value: "stringCatenate" }, length: { value: 2 } },
+    ),
+  };
+})();
 
 /**
  * Returns the codepoint at the provided position in the string
@@ -419,7 +451,7 @@ export const getCodeUnit = createCallableFunction(
  */
 export const getCodepoint = createCallableFunction(
   stringPrototype.codePointAt,
-  "getCodepoint",
+  { name: "getCodepoint" },
 );
 
 /**
@@ -429,7 +461,7 @@ export const getCodepoint = createCallableFunction(
  */
 export const getFirstSubstringIndex = createCallableFunction(
   stringPrototype.indexOf,
-  "getFirstSubstringIndex",
+  { name: "getFirstSubstringIndex" },
 );
 
 /**
@@ -439,7 +471,7 @@ export const getFirstSubstringIndex = createCallableFunction(
  */
 export const getLastSubstringIndex = createCallableFunction(
   stringPrototype.lastIndexOf,
-  "getLastSubstringIndex",
+  { name: "getLastSubstringIndex" },
 );
 
 /**
@@ -451,34 +483,79 @@ export const getLastSubstringIndex = createCallableFunction(
  */
 export const join = (() => {
   const { join: arrayJoin } = arrayPrototype;
-  const join = ($, separator = ",") =>
-    call(arrayJoin, [...$], [`${separator}`]);
+  const join = ($, separator) =>
+    call(
+      arrayJoin,
+      [...$],
+      [separator === undefined ? "," : `${separator}`],
+    );
   return join;
 })();
 
-export const {
-  /**
-   * Returns a string created from the raw value of the tagged template
-   * literal.
-   *
-   * ※ This is an alias for `String.raw`.
-   */
-  raw: rawString,
+/**
+ * Returns a string created from the raw value of the tagged template
+ * literal.
+ *
+ * ※ This is effectively an alias for `String.raw`.
+ */
+export const rawString = createArrowFunction(String.raw, {
+  name: "rawString",
+});
 
+export const {
   /**
    * Returns a string created from the provided code units.
    *
-   * ※ This is an alias for `String.fromCharCode`.
-   */
-  fromCharCode: stringFromCodeUnits,
-
-  /**
-   * Returns a string created from the provided codepoints.
+   * ※ This is effectively an alias for `String.fromCharCode`, but
+   * with the same error behaviour as `String.fromCodePoint`.
    *
-   * ※ This is an alias for `String.fromCodePoint`.
+   * ☡ This function throws an error if provided with an argument which
+   * is not an integral number from 0 to FFFF₁₆ inclusive.
    */
-  fromCodePoint: stringFromCodepoints,
-} = String;
+  stringFromCodeUnits,
+} = (() => {
+  const { fromCharCode } = String;
+  const { isInteger: isIntegralNumber } = Number;
+
+  return {
+    stringFromCodeUnits: defineOwnProperties(
+      (...codeUnits) => {
+        for (let index = 0; index < codeUnits.length; ++index) {
+          // Iterate over each provided code unit and throw if it is
+          // out of range.
+          const nextCU = +codeUnits[index];
+          if (
+            !isIntegralNumber(nextCU) || nextCU < 0 || nextCU > 0xFFFF
+          ) {
+            // The code unit is not an integral number between 0 and
+            // 0xFFFF.
+            throw new RangeError(
+              `Piscēs: Code unit out of range: ${nextCU}.`,
+            );
+          } else {
+            // The code unit is acceptable.
+            /* do nothing */
+          }
+        }
+        return call(fromCharCode, undefined, codeUnits);
+      },
+      { name: { value: "stringFromCodeUnits" }, length: { value: 1 } },
+    ),
+  };
+})();
+
+/**
+ * Returns a string created from the provided codepoints.
+ *
+ * ※ This is effectively an alias for `String.fromCodePoint`.
+ *
+ * ☡ This function throws an error if provided with an argument which
+ * is not an integral number from 0 to 10FFFF₁₆ inclusive.
+ */
+export const stringFromCodepoints = createArrowFunction(
+  String.fromCodePoint,
+  { name: "stringFromCodepoints" },
+);
 
 /**
  * Returns the result of splitting the provided value on A·S·C·I·I
@@ -503,16 +580,6 @@ export const splitOnCommas = ($) =>
     ",",
   );
 
-/**
- * Returns the result of catenating the string representations of the
- * provided values, returning a new string according to the algorithm
- * of `String::concat`.
- */
-export const stringCatenate = createCallableFunction(
-  stringPrototype.concat,
-  "stringCatenate",
-);
-
 /**
  * Returns whether the string representation of the provided value ends
  * with the provided search string according to the algorithm of
@@ -520,7 +587,7 @@ export const stringCatenate = createCallableFunction(
  */
 export const stringEndsWith = createCallableFunction(
   stringPrototype.endsWith,
-  "stringEndsWith",
+  { name: "stringEndsWith" },
 );
 
 /**
@@ -530,7 +597,7 @@ export const stringEndsWith = createCallableFunction(
  */
 export const stringIncludes = createCallableFunction(
   stringPrototype.includes,
-  "stringIncludes",
+  { name: "stringIncludes" },
 );
 
 /**
@@ -540,7 +607,7 @@ export const stringIncludes = createCallableFunction(
  */
 export const stringMatch = createCallableFunction(
   stringPrototype.match,
-  "stringMatch",
+  { name: "stringMatch" },
 );
 
 /**
@@ -550,16 +617,16 @@ export const stringMatch = createCallableFunction(
  */
 export const stringMatchAll = createCallableFunction(
   stringPrototype.matchAll,
-  "stringMatchAll",
+  { name: "stringMatchAll" },
 );
 
 /**
  * Returns the normalized form of the string representation of the
- * provided value according to the algorithm of `String::matchAll`.
+ * provided value according to the algorithm of `String::normalize`.
  */
 export const stringNormalize = createCallableFunction(
   stringPrototype.normalize,
-  "stringNormalize",
+  { name: "stringNormalize" },
 );
 
 /**
@@ -569,7 +636,7 @@ export const stringNormalize = createCallableFunction(
  */
 export const stringPadEnd = createCallableFunction(
   stringPrototype.padEnd,
-  "stringPadEnd",
+  { name: "stringPadEnd" },
 );
 
 /**
@@ -579,7 +646,7 @@ export const stringPadEnd = createCallableFunction(
  */
 export const stringPadStart = createCallableFunction(
   stringPrototype.padStart,
-  "stringPadStart",
+  { name: "stringPadStart" },
 );
 
 /**
@@ -589,7 +656,7 @@ export const stringPadStart = createCallableFunction(
  */
 export const stringRepeat = createCallableFunction(
   stringPrototype.repeat,
-  "stringRepeat",
+  { name: "stringRepeat" },
 );
 
 /**
@@ -599,7 +666,7 @@ export const stringRepeat = createCallableFunction(
  */
 export const stringReplace = createCallableFunction(
   stringPrototype.replace,
-  "stringReplace",
+  { name: "stringReplace" },
 );
 
 /**
@@ -609,7 +676,7 @@ export const stringReplace = createCallableFunction(
  */
 export const stringReplaceAll = createCallableFunction(
   stringPrototype.replaceAll,
-  "stringReplaceAll",
+  { name: "stringReplaceAll" },
 );
 
 /**
@@ -619,7 +686,7 @@ export const stringReplaceAll = createCallableFunction(
  */
 export const stringSearch = createCallableFunction(
   stringPrototype.search,
-  "stringSearch",
+  { name: "stringSearch" },
 );
 
 /**
@@ -628,7 +695,7 @@ export const stringSearch = createCallableFunction(
  */
 export const stringSlice = createCallableFunction(
   stringPrototype.slice,
-  "stringSlice",
+  { name: "stringSlice" },
 );
 
 /**
@@ -638,7 +705,7 @@ export const stringSlice = createCallableFunction(
  */
 export const stringSplit = createCallableFunction(
   stringPrototype.split,
-  "stringSplit",
+  { name: "stringSplit" },
 );
 
 /**
@@ -648,18 +715,20 @@ export const stringSplit = createCallableFunction(
  */
 export const stringStartsWith = createCallableFunction(
   stringPrototype.startsWith,
-  "stringStartsWith",
+  { name: "stringStartsWith" },
 );
 
 /**
- * Returns the `[[StringData]]` of the provided value.
+ * Returns the value of the provided string.
+ *
+ * ※ This is effectively an alias for the `String::valueOf`.
  *
- * ☡ This function will throw if the provided object does not have a
- * `[[StringData]]` internal slot.
+ * ☡ This function throws if the provided argument is not a string and
+ * does not have a `[[StringData]]` slot.
  */
 export const stringValue = createCallableFunction(
   stringPrototype.valueOf,
-  "stringValue",
+  { name: "stringValue" },
 );
 
 /**
index 066770b39471f5f58f1822292744b6627a27521b..4f257f9f96f28a8d799cb0fd447c28895e429c00 100644 (file)
@@ -20,21 +20,50 @@ import {
 import {
   asciiLowercase,
   asciiUppercase,
+  characters,
   codepoints,
   codeUnits,
   getCharacter,
+  getCodepoint,
+  getCodeUnit,
+  getFirstSubstringIndex,
+  getLastSubstringIndex,
   join,
   Matcher,
+  rawString,
   scalarValues,
   scalarValueString,
   splitOnASCIIWhitespace,
   splitOnCommas,
+  stringCatenate,
+  stringEndsWith,
+  stringFromCodepoints,
+  stringFromCodeUnits,
+  stringIncludes,
+  stringMatch,
+  stringMatchAll,
+  stringNormalize,
+  stringPadEnd,
+  stringPadStart,
+  stringRepeat,
+  stringReplace,
+  stringReplaceAll,
+  stringSearch,
+  stringSlice,
+  stringSplit,
+  stringStartsWith,
+  stringValue,
   stripAndCollapseASCIIWhitespace,
   stripLeadingAndTrailingASCIIWhitespace,
+  substring,
   toString,
 } from "./string.js";
 
 describe("Matcher", () => {
+  it("[[Call]] throws an error", () => {
+    assertThrows(() => Matcher(""));
+  });
+
   it("[[Construct]] accepts a string first argument", () => {
     assert(new Matcher(""));
   });
@@ -66,6 +95,18 @@ describe("Matcher", () => {
     assertThrows(() => new Matcher("", undefined, "failure"));
   });
 
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(Matcher.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(Matcher.name, "Matcher");
+    });
+  });
+
   describe("::dotAll", () => {
     it("[[Get]] returns true when the dotAll flag is present", () => {
       assertStrictEquals(new Matcher(/(?:)/su).dotAll, true);
@@ -74,6 +115,30 @@ describe("Matcher", () => {
     it("[[Get]] returns false when the dotAll flag is not present", () => {
       assertStrictEquals(new Matcher(/(?:)/u).dotAll, false);
     });
+
+    describe(".length", () => {
+      it("[[Get]] returns the correct length", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "dotAll",
+          ).get.length,
+          0,
+        );
+      });
+    });
+
+    describe(".name", () => {
+      it("[[Get]] returns the correct name", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "dotAll",
+          ).get.name,
+          "get dotAll",
+        );
+      });
+    });
   });
 
   describe("::exec", () => {
@@ -125,6 +190,18 @@ describe("Matcher", () => {
         null,
       );
     });
+
+    describe(".length", () => {
+      it("[[Get]] returns the correct length", () => {
+        assertStrictEquals(Matcher.prototype.exec.length, 1);
+      });
+    });
+
+    describe(".name", () => {
+      it("[[Get]] returns the correct name", () => {
+        assertStrictEquals(Matcher.prototype.exec.name, "exec");
+      });
+    });
   });
 
   describe("::global", () => {
@@ -135,6 +212,30 @@ describe("Matcher", () => {
     it("[[Get]] returns false when the global flag is not present", () => {
       assertStrictEquals(new Matcher(/(?:)/u).global, false);
     });
+
+    describe(".length", () => {
+      it("[[Get]] returns the correct length", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "global",
+          ).get.length,
+          0,
+        );
+      });
+    });
+
+    describe(".name", () => {
+      it("[[Get]] returns the correct name", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "global",
+          ).get.name,
+          "get global",
+        );
+      });
+    });
   });
 
   describe("::hasIndices", () => {
@@ -145,6 +246,30 @@ describe("Matcher", () => {
     it("[[Get]] returns false when the hasIndices flag is not present", () => {
       assertStrictEquals(new Matcher(/(?:)/u).hasIndices, false);
     });
+
+    describe(".length", () => {
+      it("[[Get]] returns the correct length", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "hasIndices",
+          ).get.length,
+          0,
+        );
+      });
+    });
+
+    describe(".name", () => {
+      it("[[Get]] returns the correct name", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "hasIndices",
+          ).get.name,
+          "get hasIndices",
+        );
+      });
+    });
   });
 
   describe("::ignoreCase", () => {
@@ -155,6 +280,30 @@ describe("Matcher", () => {
     it("[[Get]] returns false when the ignoreCase flag is not present", () => {
       assertStrictEquals(new Matcher(/(?:)/u).ignoreCase, false);
     });
+
+    describe(".length", () => {
+      it("[[Get]] returns the correct length", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "ignoreCase",
+          ).get.length,
+          0,
+        );
+      });
+    });
+
+    describe(".name", () => {
+      it("[[Get]] returns the correct name", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "ignoreCase",
+          ).get.name,
+          "get ignoreCase",
+        );
+      });
+    });
   });
 
   describe("::multiline", () => {
@@ -165,6 +314,30 @@ describe("Matcher", () => {
     it("[[Get]] returns false when the multiline flag is not present", () => {
       assertStrictEquals(new Matcher(/(?:)/u).multiline, false);
     });
+
+    describe(".length", () => {
+      it("[[Get]] returns the correct length", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "multiline",
+          ).get.length,
+          0,
+        );
+      });
+    });
+
+    describe(".name", () => {
+      it("[[Get]] returns the correct name", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "multiline",
+          ).get.name,
+          "get multiline",
+        );
+      });
+    });
   });
 
   describe("::source", () => {
@@ -172,6 +345,30 @@ describe("Matcher", () => {
       assertStrictEquals(new Matcher("").source, "(?:)");
       assertStrictEquals(new Matcher(/.*/su).source, ".*");
     });
+
+    describe(".length", () => {
+      it("[[Get]] returns the correct length", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "source",
+          ).get.length,
+          0,
+        );
+      });
+    });
+
+    describe(".name", () => {
+      it("[[Get]] returns the correct name", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "source",
+          ).get.name,
+          "get source",
+        );
+      });
+    });
   });
 
   describe("::sticky", () => {
@@ -182,11 +379,35 @@ describe("Matcher", () => {
     it("[[Get]] returns false when the sticky flag is not present", () => {
       assertStrictEquals(new Matcher(/(?:)/u).sticky, false);
     });
+
+    describe(".length", () => {
+      it("[[Get]] returns the correct length", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "sticky",
+          ).get.length,
+          0,
+        );
+      });
+    });
+
+    describe(".name", () => {
+      it("[[Get]] returns the correct name", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "sticky",
+          ).get.name,
+          "get sticky",
+        );
+      });
+    });
   });
 
   describe("::toString", () => {
-    it("[[Get]] does not throw an error", () => {
-      new Matcher(/(?:)/u).toString();
+    it("[[Call]] returns the string source", () => {
+      assertStrictEquals(new Matcher(/(?:)/u).toString(), "/(?:)/u");
     });
   });
 
@@ -194,6 +415,30 @@ describe("Matcher", () => {
     it("[[Get]] returns true when the unicode flag is present", () => {
       assertStrictEquals(new Matcher(/(?:)/u).unicode, true);
     });
+
+    describe(".length", () => {
+      it("[[Get]] returns the correct length", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "unicode",
+          ).get.length,
+          0,
+        );
+      });
+    });
+
+    describe(".name", () => {
+      it("[[Get]] returns the correct name", () => {
+        assertStrictEquals(
+          Object.getOwnPropertyDescriptor(
+            Matcher.prototype,
+            "unicode",
+          ).get.name,
+          "get unicode",
+        );
+      });
+    });
   });
 
   describe("~", () => {
@@ -274,12 +519,96 @@ describe("asciiLowercase", () => {
   it("[[Call]] lowercases (just) A·S·C·I·I letters", () => {
     assertStrictEquals(asciiLowercase("aBſÆss FtɁɂß"), "abſÆss ftɁɂß");
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new asciiLowercase(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(asciiLowercase.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(asciiLowercase.name, "asciiLowercase");
+    });
+  });
 });
 
 describe("asciiUppercase", () => {
   it("[[Call]] uppercases (just) A·S·C·I·I letters", () => {
     assertStrictEquals(asciiUppercase("aBſÆss FtɁɂß"), "ABſÆSS FTɁɂß");
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new asciiUppercase(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(asciiUppercase.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(asciiUppercase.name, "asciiUppercase");
+    });
+  });
+});
+
+describe("characters", () => {
+  it("[[Call]] returns an iterable", () => {
+    assertStrictEquals(
+      typeof characters("")[Symbol.iterator],
+      "function",
+    );
+  });
+
+  it("[[Call]] returns an iterator", () => {
+    assertStrictEquals(typeof characters("").next, "function");
+  });
+
+  it("[[Call]] returns a string character iterator", () => {
+    assertStrictEquals(
+      characters("")[Symbol.toStringTag],
+      "String Character Iterator",
+    );
+  });
+
+  it("[[Call]] iterates over the characters", () => {
+    assertEquals([
+      ...characters("Ii🎙\uDFFF\uDD96\uD83C\uD800🆗☺"),
+    ], [
+      "I",
+      "i",
+      "🎙",
+      "\uDFFF",
+      "\uDD96",
+      "\uD83C",
+      "\uD800",
+      "🆗",
+      "☺",
+    ]);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new characters(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(characters.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(characters.name, "characters");
+    });
+  });
 });
 
 describe("codeUnits", () => {
@@ -318,6 +647,22 @@ describe("codeUnits", () => {
       0x263A,
     ]);
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new codeUnits(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(codeUnits.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(codeUnits.name, "codeUnits");
+    });
+  });
 });
 
 describe("codepoints", () => {
@@ -354,6 +699,22 @@ describe("codepoints", () => {
       0x263A,
     ]);
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new codepoints(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(codepoints.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(codepoints.name, "codepoints");
+    });
+  });
 });
 
 describe("getCharacter", () => {
@@ -366,8 +727,155 @@ describe("getCharacter", () => {
   });
 
   it("[[Call]] returns undefined for an out‐of‐bounds index", () => {
-    assertStrictEquals(getCharacter("Ii🎙🆗☺", -1), void {});
-    assertStrictEquals(getCharacter("Ii🎙🆗☺", 7), void {});
+    assertStrictEquals(getCharacter("Ii🎙🆗☺", -1), undefined);
+    assertStrictEquals(getCharacter("Ii🎙🆗☺", 7), undefined);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getCharacter("a", 0));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getCharacter.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(getCharacter.name, "getCharacter");
+    });
+  });
+});
+
+describe("getCodeUnit", () => {
+  it("[[Call]] returns the code unit at the provided position", () => {
+    assertStrictEquals(getCodeUnit("Ii🎙🆗☺", 4), 0xD83C);
+  });
+
+  it("[[Call]] returns a low surrogate if the provided position splits a character", () => {
+    assertStrictEquals(getCodeUnit("Ii🎙🆗☺", 5), 0xDD97);
+  });
+
+  it("[[Call]] returns undefined for an out‐of‐bounds index", () => {
+    assertStrictEquals(getCodeUnit("Ii🎙🆗☺", -1), undefined);
+    assertStrictEquals(getCodeUnit("Ii🎙🆗☺", 7), undefined);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getCodeUnit("a", 0));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getCodeUnit.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(getCodeUnit.name, "getCodeUnit");
+    });
+  });
+});
+
+describe("getCodepoint", () => {
+  it("[[Call]] returns the character at the provided position", () => {
+    assertStrictEquals(getCodepoint("Ii🎙🆗☺", 4), 0x1F197);
+  });
+
+  it("[[Call]] returns a low surrogate if the provided position splits a character", () => {
+    assertStrictEquals(getCodepoint("Ii🎙🆗☺", 5), 0xDD97);
+  });
+
+  it("[[Call]] returns undefined for an out‐of‐bounds index", () => {
+    assertStrictEquals(getCodepoint("Ii🎙🆗☺", -1), undefined);
+    assertStrictEquals(getCodepoint("Ii🎙🆗☺", 7), undefined);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getCodepoint("a", 0));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getCodepoint.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(getCodepoint.name, "getCodepoint");
+    });
+  });
+});
+
+describe("getFirstSubstringIndex", () => {
+  it("[[Call]] returns the index of the first match", () => {
+    assertStrictEquals(getFirstSubstringIndex("Ii🎙🆗☺🆗", "🆗"), 4);
+  });
+
+  it("[[Call]] returns −1 if no match is found", () => {
+    assertStrictEquals(getFirstSubstringIndex("Ii🎙🆗☺🆗", "🆗🆖"), -1);
+  });
+
+  it("[[Call]] returns 0 when provided with an empty string", () => {
+    assertStrictEquals(getFirstSubstringIndex("Ii🎙🆗☺🆗", ""), 0);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getFirstSubstringIndex("", ""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getFirstSubstringIndex.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        getFirstSubstringIndex.name,
+        "getFirstSubstringIndex",
+      );
+    });
+  });
+});
+
+describe("getLastSubstringIndex", () => {
+  it("[[Call]] returns the index of the first match", () => {
+    assertStrictEquals(getLastSubstringIndex("Ii🎙🆗☺🆗", "🆗"), 7);
+  });
+
+  it("[[Call]] returns −1 if no match is found", () => {
+    assertStrictEquals(getLastSubstringIndex("Ii🎙🆗☺🆗", "🆖🆗"), -1);
+  });
+
+  it("[[Call]] returns the length when provided with an empty string", () => {
+    assertStrictEquals(
+      getLastSubstringIndex("Ii🎙🆗☺🆗", ""),
+      "Ii🎙🆗☺🆗".length,
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getLastSubstringIndex("", ""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getLastSubstringIndex.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        getLastSubstringIndex.name,
+        "getLastSubstringIndex",
+      );
+    });
   });
 });
 
@@ -386,6 +894,44 @@ describe("join", () => {
       "☂☂☂",
     );
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new join([]));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(join.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(join.name, "join");
+    });
+  });
+});
+
+describe("rawString", () => {
+  it("[[Call]] acts like String.raw", () => {
+    assertStrictEquals(rawString`\nraw${" string"}`, "\\nraw string");
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new rawString(["string"]));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(rawString.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(rawString.name, "rawString");
+    });
+  });
 });
 
 describe("scalarValueString", () => {
@@ -395,6 +941,22 @@ describe("scalarValueString", () => {
       "Ii🎙\uFFFD\uFFFD\uFFFD\uFFFD🆗☺",
     );
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new scalarValueString(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(scalarValueString.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(scalarValueString.name, "scalarValueString");
+    });
+  });
 });
 
 describe("scalarValues", () => {
@@ -431,6 +993,22 @@ describe("scalarValues", () => {
       0x263A,
     ]);
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new scalarValues(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(scalarValues.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(scalarValues.name, "scalarValues");
+    });
+  });
 });
 
 describe("splitOnASCIIWhitespace", () => {
@@ -499,6 +1077,25 @@ describe("splitOnASCIIWhitespace", () => {
       ["🅰️", "🅱️", "🆎", "🅾️"],
     );
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new splitOnASCIIWhitespace(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(splitOnASCIIWhitespace.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        splitOnASCIIWhitespace.name,
+        "splitOnASCIIWhitespace",
+      );
+    });
+  });
 });
 
 describe("splitOnCommas", () => {
@@ -544,20 +1141,655 @@ describe("splitOnCommas", () => {
       ["", "", "", ""],
     );
   });
-});
 
-describe("stripAndCollapseASCIIWhitespace", () => {
-  it("[[Call]] collapses mixed inner whitespace", () => {
-    assertEquals(
-      stripAndCollapseASCIIWhitespace("🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️"),
-      "🅰️ 🅱️ 🆎 🅾️",
-    );
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new splitOnCommas(""));
   });
 
-  it("[[Call]] trims leading and trailing whitespace", () => {
-    assertStrictEquals(
-      stripAndCollapseASCIIWhitespace(
-        "\f\r\n\r\n \n\t\f 🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️\n\f",
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(splitOnCommas.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(splitOnCommas.name, "splitOnCommas");
+    });
+  });
+});
+
+describe("stringCatenate", () => {
+  it("[[Call]] catenates the values", () => {
+    assertStrictEquals(stringCatenate("the", " values"), "the values");
+  });
+
+  it("[[Call]] returns an empty string when called with no values", () => {
+    assertStrictEquals(stringCatenate(), "");
+  });
+
+  it('[[Call]] uses "undefined" when explicitly provided undefined', () => {
+    assertStrictEquals(
+      stringCatenate(undefined, undefined),
+      "undefinedundefined",
+    );
+  });
+
+  it('[[Call]] uses "null" when provided null', () => {
+    assertStrictEquals(stringCatenate(null, null), "nullnull");
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringCatenate());
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringCatenate.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringCatenate.name, "stringCatenate");
+    });
+  });
+});
+
+describe("stringEndsWith", () => {
+  it("[[Call]] returns whether the string ends with the thing", () => {
+    assertStrictEquals(
+      stringEndsWith("very success", " success"),
+      true,
+    );
+    assertStrictEquals(stringEndsWith("very fail", " success"), false);
+  });
+
+  it("[[Call]] accepts an offset", () => {
+    assertStrictEquals(
+      stringEndsWith("very successful", " success", 12),
+      true,
+    );
+  });
+
+  it("[[Call]] returns true for an empty string test", () => {
+    assertStrictEquals(stringEndsWith("", ""), true);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringEndsWith("", ""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringEndsWith.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringEndsWith.name, "stringEndsWith");
+    });
+  });
+});
+
+describe("stringFromCodeUnits", () => {
+  it("[[Call]] makes the string", () => {
+    assertStrictEquals(
+      stringFromCodeUnits(0xD83C, 0xDD97),
+      "🆗",
+    );
+  });
+
+  it("[[Call]] throws with non‐integral arguments", () => {
+    assertThrows(() => stringFromCodeUnits(NaN));
+    assertThrows(() => stringFromCodeUnits(Infinity));
+    assertThrows(() => stringFromCodeUnits(0.1));
+  });
+
+  it("[[Call]] throws with arguments out of range", () => {
+    assertThrows(() => stringFromCodeUnits(-1));
+    assertThrows(() => stringFromCodeUnits(0x10000));
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringFromCodeUnits([]));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringFromCodeUnits.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        stringFromCodeUnits.name,
+        "stringFromCodeUnits",
+      );
+    });
+  });
+});
+
+describe("stringFromCodepoints", () => {
+  it("[[Call]] makes the string", () => {
+    assertStrictEquals(stringFromCodepoints(0x1F197), "🆗");
+  });
+
+  it("[[Call]] throws with non‐integral arguments", () => {
+    assertThrows(() => stringFromCodepoints(NaN));
+    assertThrows(() => stringFromCodepoints(Infinity));
+    assertThrows(() => stringFromCodepoints(0.1));
+  });
+
+  it("[[Call]] throws with arguments out of range", () => {
+    assertThrows(() => stringFromCodepoints(-1));
+    assertThrows(() => stringFromCodepoints(0x110000));
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringFromCodepoints([]));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringFromCodepoints.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        stringFromCodepoints.name,
+        "stringFromCodepoints",
+      );
+    });
+  });
+});
+
+describe("stringIncludes", () => {
+  it("[[Call]] returns whether the string includes the thing", () => {
+    assertStrictEquals(
+      stringIncludes("very success full", " success "),
+      true,
+    );
+    assertStrictEquals(
+      stringIncludes("very fail full", " success "),
+      false,
+    );
+  });
+
+  it("[[Call]] accepts an offset", () => {
+    assertStrictEquals(
+      stringIncludes("maybe success full", " success ", 4),
+      true,
+    );
+    assertStrictEquals(
+      stringIncludes("maybe success full", " success ", 5),
+      true,
+    );
+    assertStrictEquals(
+      stringIncludes("maybe success full", " success ", 6),
+      false,
+    );
+  });
+
+  it("[[Call]] returns true for an empty string test", () => {
+    assertStrictEquals(stringIncludes("", ""), true);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringIncludes("", ""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringIncludes.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringIncludes.name, "stringIncludes");
+    });
+  });
+});
+
+describe("stringMatch", () => {
+  it("[[Call]] does the match akin to String::match", () => {
+    assertEquals(
+      [...stringMatch("very success full", /([sc]+[ue]?)+/)],
+      ["success", "ss"],
+    );
+    assertEquals(
+      [...stringMatch("very success full", /([sc]+)[ue]?/g)],
+      ["su", "cce", "ss"],
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringMatch("", /(?:)/));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringMatch.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringMatch.name, "stringMatch");
+    });
+  });
+});
+
+describe("stringMatchAll", () => {
+  it("[[Call]] does the match akin to String::matchAll", () => {
+    assertEquals(
+      [...stringMatchAll("very success full", /([sc]+)[ue]?/g)].map((
+        match,
+      ) => [...match]),
+      [["su", "s"], ["cce", "cc"], ["ss", "ss"]],
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringMatchAll("", /(?:)/g));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringMatchAll.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringMatchAll.name, "stringMatchAll");
+    });
+  });
+});
+
+describe("stringNormalize", () => {
+  it("[[Call]] normalizes the string properly", () => {
+    assertStrictEquals(stringNormalize("ẛ", "NFC"), "\u1E9B");
+    assertStrictEquals(stringNormalize("ẛ", "NFD"), "\u017F\u0307");
+    assertStrictEquals(stringNormalize("ẛ", "NFKC"), "\u1E61");
+    assertStrictEquals(stringNormalize("ẛ", "NFKD"), "\u0073\u0307");
+  });
+
+  it("[[Call]] assumes NFC", () => {
+    assertStrictEquals(stringNormalize("\u017F\u0307"), "\u1E9B");
+  });
+
+  it("[[Call]] throws with an invalid form", () => {
+    assertThrows(() => stringNormalize("", "NFB"));
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringNormalize("", "NFC"));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringNormalize.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringNormalize.name, "stringNormalize");
+    });
+  });
+});
+
+describe("stringPadEnd", () => {
+  it("[[Call]] pads the end of the string", () => {
+    assertStrictEquals(stringPadEnd("xx", 3), "xx ");
+    assertStrictEquals(stringPadEnd("xx", 3, "o"), "xxo");
+    assertStrictEquals(stringPadEnd("", 3, "xo"), "xox");
+    assertStrictEquals(stringPadEnd("xx", 3, ""), "xx");
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringPadEnd("", 1));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringPadEnd.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringPadEnd.name, "stringPadEnd");
+    });
+  });
+});
+
+describe("stringPadStart", () => {
+  it("[[Call]] pads the start of the string", () => {
+    assertStrictEquals(stringPadStart("xx", 3), " xx");
+    assertStrictEquals(stringPadStart("xx", 3, "o"), "oxx");
+    assertStrictEquals(stringPadStart("", 3, "xo"), "xox");
+    assertStrictEquals(stringPadStart("xx", 3, ""), "xx");
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringPadStart("", 1));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringPadStart.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringPadStart.name, "stringPadStart");
+    });
+  });
+});
+
+describe("stringRepeat", () => {
+  it("[[Call]] repeats the string", () => {
+    assertStrictEquals(stringRepeat("xx", 3), "xxxxxx");
+    assertStrictEquals(stringRepeat("", 3), "");
+    assertStrictEquals(stringRepeat("xx", 0), "");
+  });
+
+  it("[[Call]] throws for negative repititions", () => {
+    assertThrows(() => stringRepeat("", -1));
+  });
+
+  it("[[Call]] throws for infinite repititions", () => {
+    assertThrows(() => stringRepeat("", Infinity));
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringRepeat("", 1));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringRepeat.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringRepeat.name, "stringRepeat");
+    });
+  });
+});
+
+describe("stringReplace", () => {
+  it("[[Call]] does the replacement akin to String::replace", () => {
+    assertStrictEquals(
+      stringReplace("it’s a failure", "failure", "success"),
+      "it’s a success",
+    );
+    assertStrictEquals(
+      stringReplace(
+        "very success full",
+        /([sc]+)[ue]?/,
+        ($) => $.length,
+      ),
+      "very 2ccess full",
+    );
+    assertStrictEquals(
+      stringReplace(
+        "very success full",
+        /([sc]+)[ue]?/g,
+        (...$s) =>
+          `${$s[0].length}`.repeat($s[1].length) +
+          $s[0].substring($s[1].length),
+      ),
+      "very 2u33e22 full",
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringReplace("", /(?:)/, ""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringReplace.length, 3);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringReplace.name, "stringReplace");
+    });
+  });
+});
+
+describe("stringReplaceAll", () => {
+  it("[[Call]] does the match akin to String::replaceAll", () => {
+    assertStrictEquals(
+      stringReplaceAll("it’s a failure failure", "failure", "success"),
+      "it’s a success success",
+    );
+    assertStrictEquals(
+      stringReplaceAll(
+        "very success full",
+        /([sc]+)[ue]?/g,
+        (...$s) =>
+          `${$s[0].length}`.repeat($s[1].length) +
+          $s[0].substring($s[1].length),
+      ),
+      "very 2u33e22 full",
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringReplaceAll("", /(?:)/g));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringReplaceAll.length, 3);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringReplaceAll.name, "stringReplaceAll");
+    });
+  });
+});
+
+describe("stringSearch", () => {
+  it("[[Call]] does the search akin to String::search", () => {
+    assertStrictEquals(
+      stringSearch("very success full", /([sc]+)[ue]?/),
+      5,
+    );
+    assertStrictEquals(
+      stringSearch("very fail full", /([sc]+)[ue]?/),
+      -1,
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringSearch("", /(?:)/));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringSearch.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringSearch.name, "stringSearch");
+    });
+  });
+});
+
+describe("stringSlice", () => {
+  it("[[Call]] slices the string akin to String::search", () => {
+    assertStrictEquals(
+      stringSlice("very success full", 5, 12),
+      "success",
+    );
+    assertStrictEquals(
+      stringSlice("very success full", -12, -5),
+      "success",
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringSlice("", 0, 0));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringSlice.length, 3);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringSlice.name, "stringSlice");
+    });
+  });
+});
+
+describe("stringSplit", () => {
+  it("[[Call]] splits the string akin to String::split", () => {
+    assertEquals(stringSplit("success", ""), [
+      "s",
+      "u",
+      "c",
+      "c",
+      "e",
+      "s",
+      "s",
+    ]);
+    assertEquals(stringSplit("success", /(?<=[aeiou])(?=[^aeiou])/), [
+      "su",
+      "cce",
+      "ss",
+    ]);
+    assertEquals(stringSplit("success", "failure"), ["success"]);
+  });
+
+  it("[[Call]] recognizes a limit", () => {
+    assertEquals(stringSplit("success", "", 4), ["s", "u", "c", "c"]);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringSplit("", ""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringSplit.length, 3);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringSplit.name, "stringSplit");
+    });
+  });
+});
+
+describe("stringStartsWith", () => {
+  it("[[Call]] returns whether the string starts with the thing", () => {
+    assertStrictEquals(
+      stringStartsWith("success is had", "success "),
+      true,
+    );
+    assertStrictEquals(
+      stringStartsWith("no success is had", "success "),
+      false,
+    );
+  });
+
+  it("[[Call]] accepts an offset", () => {
+    assertStrictEquals(
+      stringStartsWith("much success is had", "success ", 5),
+      true,
+    );
+  });
+
+  it("[[Call]] returns true for an empty string test", () => {
+    assertStrictEquals(stringEndsWith("", ""), true);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringStartsWith("", ""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringStartsWith.length, 2);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringStartsWith.name, "stringStartsWith");
+    });
+  });
+});
+
+describe("stringStartsWith", () => {
+  it("[[Call]] returns the string value of a string literal", () => {
+    assertStrictEquals(stringValue("success"), "success");
+  });
+
+  it("[[Call]] returns the string value of a string object", () => {
+    const string = new String("success");
+    Object.defineProperties(string, {
+      toString: { value: () => "failure" },
+      valueOf: { value: () => "failure" },
+    });
+    assertStrictEquals(stringValue(string), "success");
+  });
+
+  it("[[Call]] throws for non‐strings", () => {
+    assertThrows(() => stringValue(Object.create(String.prototype)));
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stringValue(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stringValue.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(stringValue.name, "stringValue");
+    });
+  });
+});
+
+describe("stripAndCollapseASCIIWhitespace", () => {
+  it("[[Call]] collapses mixed inner whitespace", () => {
+    assertEquals(
+      stripAndCollapseASCIIWhitespace("🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️"),
+      "🅰️ 🅱️ 🆎 🅾️",
+    );
+  });
+
+  it("[[Call]] trims leading and trailing whitespace", () => {
+    assertStrictEquals(
+      stripAndCollapseASCIIWhitespace(
+        "\f\r\n\r\n \n\t\f 🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️\n\f",
       ),
       "🅰️ 🅱️ 🆎 🅾️",
     );
@@ -576,6 +1808,25 @@ describe("stripAndCollapseASCIIWhitespace", () => {
       "a\u202F\u205F\xa0\v\0\bb",
     );
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stripAndCollapseASCIIWhitespace(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(stripAndCollapseASCIIWhitespace.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        stripAndCollapseASCIIWhitespace.name,
+        "stripAndCollapseASCIIWhitespace",
+      );
+    });
+  });
 });
 
 describe("stripLeadingAndTrailingASCIIWhitespace", () => {
@@ -610,6 +1861,57 @@ describe("stripLeadingAndTrailingASCIIWhitespace", () => {
       "a   b",
     );
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new stripLeadingAndTrailingASCIIWhitespace(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(
+        stripLeadingAndTrailingASCIIWhitespace.length,
+        1,
+      );
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        stripLeadingAndTrailingASCIIWhitespace.name,
+        "stripLeadingAndTrailingASCIIWhitespace",
+      );
+    });
+  });
+});
+
+describe("substring", () => {
+  it("[[Call]] returns the substring", () => {
+    assertStrictEquals(
+      substring("success", 0),
+      "success",
+    );
+    assertStrictEquals(
+      substring("very success full", 5, 12),
+      "success",
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new substring("", 0));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(substring.length, 3);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(substring.name, "substring");
+    });
+  });
 });
 
 describe("toString", () => {
@@ -627,4 +1929,20 @@ describe("toString", () => {
   it("[[Call]] throws when provided a symbol", () => {
     assertThrows(() => toString(Symbol()));
   });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new toString(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(toString.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(toString.name, "toString");
+    });
+  });
 });
This page took 0.087262 seconds and 4 git commands to generate.