]> Lady’s Gitweb - Pisces/blobdiff - string.test.js
Add methods for own entries and values to object.js
[Pisces] / string.test.js
index 52e848c3b75bcde588593512b66137d20ce34c22..ee0e2097328535d6cbd1af583152d62ae74062b2 100644 (file)
@@ -1,7 +1,7 @@
 // ♓🌟 Piscēs ∷ string.test.js
 // ====================================================================
 //
-// Copyright © 2022 Lady [@ Lady’s Computer].
+// Copyright © 2022–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
@@ -10,7 +10,6 @@
 import {
   assert,
   assertEquals,
-  assertSpyCall,
   assertSpyCalls,
   assertStrictEquals,
   assertThrows,
@@ -21,21 +20,53 @@ import {
 import {
   asciiLowercase,
   asciiUppercase,
+  canonicalNumericIndexString,
+  characters,
   codepoints,
   codeUnits,
   getCharacter,
+  getCodepoint,
+  getCodeUnit,
+  getFirstSubstringIndex,
+  getLastSubstringIndex,
+  isArrayIndexString,
+  isIntegerIndexString,
   join,
   Matcher,
+  rawString,
   scalarValues,
-  scalarValueString,
-  splitOnASCIIWhitespace,
+  splitOnAsciiWhitespace,
   splitOnCommas,
-  stripAndCollapseASCIIWhitespace,
-  stripLeadingAndTrailingASCIIWhitespace,
+  stringCatenate,
+  stringEndsWith,
+  stringFromCodepoints,
+  stringFromCodeUnits,
+  stringIncludes,
+  stringMatch,
+  stringMatchAll,
+  stringNormalize,
+  stringPadEnd,
+  stringPadStart,
+  stringRepeat,
+  stringReplace,
+  stringReplaceAll,
+  stringSearch,
+  stringSlice,
+  stringSplit,
+  stringStartsWith,
+  stringValue,
+  stripAndCollapseAsciiWhitespace,
+  stripLeadingAndTrailingAsciiWhitespace,
+  substring,
+  toScalarValueString,
   toString,
 } from "./string.js";
 
 describe("Matcher", () => {
+  it("[[Call]] throws an error", () => {
+    assertThrows(() => Matcher(""));
+  });
+
   it("[[Construct]] accepts a string first argument", () => {
     assert(new Matcher(""));
   });
@@ -67,6 +98,24 @@ 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("::constructor", () => {
+    it("[[Get]] returns the same constructor", () => {
+      assertStrictEquals(new Matcher(/(?:)/su).constructor, Matcher);
+    });
+  });
+
   describe("::dotAll", () => {
     it("[[Get]] returns true when the dotAll flag is present", () => {
       assertStrictEquals(new Matcher(/(?:)/su).dotAll, true);
@@ -75,6 +124,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", () => {
@@ -95,17 +168,18 @@ describe("Matcher", () => {
 
     it("[[Call]] calls the constraint if the match succeeds", () => {
       const constraint = spy((_) => true);
-      const matcher = new Matcher(".*", undefined, constraint);
-      matcher.exec({
+      const matcher = new Matcher("(.).*", undefined, constraint);
+      const result = matcher.exec({
         toString() {
           return "etaoin";
         },
       });
+      assertEquals([...result], ["etaoin", "e"]);
       assertSpyCalls(constraint, 1);
-      assertSpyCall(constraint, 0, {
-        args: ["etaoin", matcher],
-        self: undefined,
-      });
+      assertStrictEquals(constraint.calls[0].args[0], "etaoin");
+      assertEquals([...constraint.calls[0].args[1]], ["etaoin", "e"]);
+      assertStrictEquals(constraint.calls[0].args[2], matcher);
+      assertStrictEquals(constraint.calls[0].self, undefined);
     });
 
     it("[[Call]] does not call the constraint if the match fails", () => {
@@ -125,6 +199,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 +221,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 +255,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 +289,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 +323,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 +354,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,12 +388,66 @@ 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("[[Call]] returns the string source", () => {
+      assertStrictEquals(new Matcher(/(?:)/u).toString(), "/(?:)/u");
+    });
   });
 
   describe("::unicode", () => {
     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("~", () => {
@@ -204,13 +464,13 @@ describe("Matcher", () => {
 
     it("[[Call]] calls the constraint if the match succeeds", () => {
       const constraint = spy((_) => true);
-      const matcher = new Matcher(".*", undefined, constraint);
+      const matcher = new Matcher("(.).*", undefined, constraint);
       matcher("etaoin");
       assertSpyCalls(constraint, 1);
-      assertSpyCall(constraint, 0, {
-        args: ["etaoin", matcher],
-        self: undefined,
-      });
+      assertStrictEquals(constraint.calls[0].args[0], "etaoin");
+      assertEquals([...constraint.calls[0].args[1]], ["etaoin", "e"]);
+      assertStrictEquals(constraint.calls[0].args[2], matcher);
+      assertStrictEquals(constraint.calls[0].self, undefined);
     });
 
     it("[[Call]] does not call the constraint if the match fails", () => {
@@ -268,12 +528,149 @@ 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("canonicalNumericIndexString", () => {
+  it("[[Call]] returns undefined for nonstrings", () => {
+    assertStrictEquals(canonicalNumericIndexString(1), void {});
+  });
+
+  it("[[Call]] returns undefined for noncanonical strings", () => {
+    assertStrictEquals(canonicalNumericIndexString(""), void {});
+    assertStrictEquals(canonicalNumericIndexString("01"), void {});
+    assertStrictEquals(
+      canonicalNumericIndexString("9007199254740993"),
+      void {},
+    );
+  });
+
+  it('[[Call]] returns -0 for "-0"', () => {
+    assertStrictEquals(canonicalNumericIndexString("-0"), -0);
+  });
+
+  it("[[Call]] returns the corresponding number for canonical strings", () => {
+    assertStrictEquals(canonicalNumericIndexString("0"), 0);
+    assertStrictEquals(canonicalNumericIndexString("-0.25"), -0.25);
+    assertStrictEquals(
+      canonicalNumericIndexString("9007199254740992"),
+      9007199254740992,
+    );
+    assertStrictEquals(canonicalNumericIndexString("NaN"), 0 / 0);
+    assertStrictEquals(canonicalNumericIndexString("Infinity"), 1 / 0);
+    assertStrictEquals(
+      canonicalNumericIndexString("-Infinity"),
+      -1 / 0,
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new canonicalNumericIndexString(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(canonicalNumericIndexString.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        canonicalNumericIndexString.name,
+        "canonicalNumericIndexString",
+      );
+    });
+  });
+});
+
+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", () => {
@@ -288,10 +685,10 @@ describe("codeUnits", () => {
     assertStrictEquals(typeof codeUnits("").next, "function");
   });
 
-  it("[[Call]] returns a string code value iterator", () => {
+  it("[[Call]] returns a string code unit iterator", () => {
     assertStrictEquals(
       codeUnits("")[Symbol.toStringTag],
-      "String Code Value Iterator",
+      "String Code Unit Iterator",
     );
   });
 
@@ -312,6 +709,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", () => {
@@ -326,10 +739,10 @@ describe("codepoints", () => {
     assertStrictEquals(typeof codepoints("").next, "function");
   });
 
-  it("[[Call]] returns a string code value iterator", () => {
+  it("[[Call]] returns a string codepoint iterator", () => {
     assertStrictEquals(
       codepoints("")[Symbol.toStringTag],
-      "String Code Value Iterator",
+      "String Codepoint Iterator",
     );
   });
 
@@ -348,6 +761,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", () => {
@@ -360,197 +789,1168 @@ 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);
   });
-});
 
-describe("join", () => {
-  it("[[Call]] joins the provided iterator with the provided separartor", () => {
-    assertStrictEquals(join([1, 2, 3, 4].values(), "☂"), "1☂2☂3☂4");
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getCharacter("a", 0));
   });
 
-  it('[[Call]] uses "," if no separator is provided', () => {
-    assertStrictEquals(join([1, 2, 3, 4].values()), "1,2,3,4");
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getCharacter.length, 2);
+    });
   });
 
-  it("[[Call]] uses the empty sting for nullish values", () => {
-    assertStrictEquals(
-      join([null, , null, undefined].values(), "☂"),
-      "☂☂☂",
-    );
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(getCharacter.name, "getCharacter");
+    });
   });
 });
 
-describe("scalarValueString", () => {
-  it("[[Call]] replaces invalid values", () => {
-    assertStrictEquals(
-      scalarValueString("Ii🎙\uDFFF\uDD96\uD83C\uD800🆗☺"),
-      "Ii🎙\uFFFD\uFFFD\uFFFD\uFFFD🆗☺",
-    );
+describe("getCodeUnit", () => {
+  it("[[Call]] returns the code unit at the provided position", () => {
+    assertStrictEquals(getCodeUnit("Ii🎙🆗☺", 4), 0xD83C);
   });
-});
 
-describe("scalarValues", () => {
-  it("[[Call]] returns an iterable", () => {
-    assertStrictEquals(
-      typeof scalarValues("")[Symbol.iterator],
-      "function",
-    );
+  it("[[Call]] returns a low surrogate if the provided position splits a character", () => {
+    assertStrictEquals(getCodeUnit("Ii🎙🆗☺", 5), 0xDD97);
   });
 
-  it("[[Call]] returns an iterator", () => {
-    assertStrictEquals(typeof scalarValues("").next, "function");
+  it("[[Call]] returns undefined for an out‐of‐bounds index", () => {
+    assertStrictEquals(getCodeUnit("Ii🎙🆗☺", -1), undefined);
+    assertStrictEquals(getCodeUnit("Ii🎙🆗☺", 7), undefined);
   });
 
-  it("[[Call]] returns a string code value iterator", () => {
-    assertStrictEquals(
-      scalarValues("")[Symbol.toStringTag],
-      "String Code Value Iterator",
-    );
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getCodeUnit("a", 0));
   });
 
-  it("[[Call]] iterates over the scalar values", () => {
-    assertEquals([
-      ...scalarValues("Ii🎙\uDFFF\uDD96\uD83C\uD800🆗☺"),
-    ], [
-      0x49,
-      0x69,
-      0x1F399,
-      0xFFFD,
-      0xFFFD,
-      0xFFFD,
-      0xFFFD,
-      0x1F197,
-      0x263A,
-    ]);
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getCodeUnit.length, 2);
+    });
   });
-});
 
-describe("splitOnASCIIWhitespace", () => {
-  it("[[Call]] splits on sequences of spaces", () => {
-    assertEquals(
-      splitOnASCIIWhitespace("🅰️   🅱️ 🆎  🅾️"),
-      ["🅰️", "🅱️", "🆎", "🅾️"],
-    );
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(getCodeUnit.name, "getCodeUnit");
+    });
   });
+});
 
-  it("[[Call]] splits on sequences of tabs", () => {
-    assertEquals(
-      splitOnASCIIWhitespace("🅰️\t\t\t🅱️\t🆎\t\t🅾️"),
-      ["🅰️", "🅱️", "🆎", "🅾️"],
-    );
+describe("getCodepoint", () => {
+  it("[[Call]] returns the character at the provided position", () => {
+    assertStrictEquals(getCodepoint("Ii🎙🆗☺", 4), 0x1F197);
   });
 
-  it("[[Call]] splits on sequences of carriage returns", () => {
-    assertEquals(
-      splitOnASCIIWhitespace("🅰️\r\r\r🅱️\r🆎\r\r🅾️"),
-      ["🅰️", "🅱️", "🆎", "🅾️"],
-    );
+  it("[[Call]] returns a low surrogate if the provided position splits a character", () => {
+    assertStrictEquals(getCodepoint("Ii🎙🆗☺", 5), 0xDD97);
   });
 
-  it("[[Call]] splits on sequences of newlines", () => {
-    assertEquals(
-      splitOnASCIIWhitespace("🅰️\r\r\r🅱️\r🆎\r\r🅾️"),
-      ["🅰️", "🅱️", "🆎", "🅾️"],
-    );
+  it("[[Call]] returns undefined for an out‐of‐bounds index", () => {
+    assertStrictEquals(getCodepoint("Ii🎙🆗☺", -1), undefined);
+    assertStrictEquals(getCodepoint("Ii🎙🆗☺", 7), undefined);
   });
 
-  it("[[Call]] splits on sequences of form feeds", () => {
-    assertEquals(
-      splitOnASCIIWhitespace("🅰️\f\f\f🅱️\f🆎\f\f🅾️"),
-      ["🅰️", "🅱️", "🆎", "🅾️"],
-    );
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getCodepoint("a", 0));
   });
 
-  it("[[Call]] splits on mixed whitespace", () => {
-    assertEquals(
-      splitOnASCIIWhitespace("🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️"),
-      ["🅰️", "🅱️", "🆎", "🅾️"],
-    );
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getCodepoint.length, 2);
+    });
   });
 
-  it("[[Call]] returns an array of just the empty string for the empty string", () => {
-    assertEquals(splitOnASCIIWhitespace(""), [""]);
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(getCodepoint.name, "getCodepoint");
+    });
   });
+});
 
-  it("[[Call]] returns a single token if there are no spaces", () => {
-    assertEquals(splitOnASCIIWhitespace("abcd"), ["abcd"]);
+describe("getFirstSubstringIndex", () => {
+  it("[[Call]] returns the index of the first match", () => {
+    assertStrictEquals(getFirstSubstringIndex("Ii🎙🆗☺🆗", "🆗"), 4);
   });
 
-  it("[[Call]] does not split on other kinds of whitespace", () => {
-    assertEquals(
-      splitOnASCIIWhitespace("a\u202F\u205F\xa0\v\0\bb"),
-      ["a\u202F\u205F\xa0\v\0\bb"],
-    );
+  it("[[Call]] returns −1 if no match is found", () => {
+    assertStrictEquals(getFirstSubstringIndex("Ii🎙🆗☺🆗", "🆗🆖"), -1);
   });
 
-  it("[[Call]] trims leading and trailing whitespace", () => {
-    assertEquals(
-      splitOnASCIIWhitespace(
-        "\f\r\n\r\n \n\t🅰️\f \t\n🅱️\r🆎\n\f🅾️\n\f",
-      ),
-      ["🅰️", "🅱️", "🆎", "🅾️"],
-    );
+  it("[[Call]] returns 0 when provided with an empty string", () => {
+    assertStrictEquals(getFirstSubstringIndex("Ii🎙🆗☺🆗", ""), 0);
   });
-});
 
-describe("splitOnCommas", () => {
-  it("[[Call]] splits on commas", () => {
-    assertEquals(
-      splitOnCommas("🅰️,🅱️,🆎,🅾️"),
-      ["🅰️", "🅱️", "🆎", "🅾️"],
-    );
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new getFirstSubstringIndex("", ""));
   });
 
-  it("[[Call]] returns an array of just the empty string for the empty string", () => {
-    assertEquals(splitOnCommas(""), [""]);
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(getFirstSubstringIndex.length, 2);
+    });
   });
 
-  it("[[Call]] returns a single token if there are no commas", () => {
+  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",
+      );
+    });
+  });
+});
+
+describe("isArrayIndexString", () => {
+  it("[[Call]] returns false for nonstrings", () => {
+    assertStrictEquals(isArrayIndexString(1), false);
+  });
+
+  it("[[Call]] returns false for noncanonical strings", () => {
+    assertStrictEquals(isArrayIndexString(""), false);
+    assertStrictEquals(isArrayIndexString("01"), false);
+    assertStrictEquals(isArrayIndexString("9007199254740993"), false);
+  });
+
+  it("[[Call]] returns false for nonfinite numbers", () => {
+    assertStrictEquals(isArrayIndexString("NaN"), false);
+    assertStrictEquals(isArrayIndexString("Infinity"), false);
+    assertStrictEquals(isArrayIndexString("-Infinity"), false);
+  });
+
+  it("[[Call]] returns false for negative numbers", () => {
+    assertStrictEquals(isArrayIndexString("-0"), false);
+    assertStrictEquals(isArrayIndexString("-1"), false);
+  });
+
+  it("[[Call]] returns false for nonintegers", () => {
+    assertStrictEquals(isArrayIndexString("0.25"), false);
+    assertStrictEquals(isArrayIndexString("1.1"), false);
+  });
+
+  it("[[Call]] returns false for numbers greater than or equal to -1 >>> 0", () => {
+    assertStrictEquals(isArrayIndexString(String(-1 >>> 0)), false);
+    assertStrictEquals(
+      isArrayIndexString(String((-1 >>> 0) + 1)),
+      false,
+    );
+  });
+
+  it("[[Call]] returns true for array lengths less than -1 >>> 0", () => {
+    assertStrictEquals(isArrayIndexString("0"), true);
+    assertStrictEquals(
+      isArrayIndexString(String((-1 >>> 0) - 1)),
+      true,
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new isArrayIndexString("0"));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(isArrayIndexString.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        isArrayIndexString.name,
+        "isArrayIndexString",
+      );
+    });
+  });
+});
+
+describe("isIntegerIndexString", () => {
+  it("[[Call]] returns false for nonstrings", () => {
+    assertStrictEquals(isIntegerIndexString(1), false);
+  });
+
+  it("[[Call]] returns false for noncanonical strings", () => {
+    assertStrictEquals(isIntegerIndexString(""), false);
+    assertStrictEquals(isIntegerIndexString("01"), false);
+    assertStrictEquals(
+      isIntegerIndexString("9007199254740993"),
+      false,
+    );
+  });
+
+  it("[[Call]] returns false for nonfinite numbers", () => {
+    assertStrictEquals(isIntegerIndexString("NaN"), false);
+    assertStrictEquals(isIntegerIndexString("Infinity"), false);
+    assertStrictEquals(isIntegerIndexString("-Infinity"), false);
+  });
+
+  it("[[Call]] returns false for negative numbers", () => {
+    assertStrictEquals(isIntegerIndexString("-0"), false);
+    assertStrictEquals(isIntegerIndexString("-1"), false);
+  });
+
+  it("[[Call]] returns false for nonintegers", () => {
+    assertStrictEquals(isIntegerIndexString("0.25"), false);
+    assertStrictEquals(isIntegerIndexString("1.1"), false);
+  });
+
+  it("[[Call]] returns false for numbers greater than or equal to 2 ** 53", () => {
+    assertStrictEquals(
+      isIntegerIndexString("9007199254740992"),
+      false,
+    );
+  });
+
+  it("[[Call]] returns true for safe canonical integer strings", () => {
+    assertStrictEquals(isIntegerIndexString("0"), true);
+    assertStrictEquals(isIntegerIndexString("9007199254740991"), true);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new isIntegerIndexString("0"));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(isIntegerIndexString.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        isIntegerIndexString.name,
+        "isIntegerIndexString",
+      );
+    });
+  });
+});
+
+describe("join", () => {
+  it("[[Call]] joins the provided iterator with the provided separartor", () => {
+    assertStrictEquals(join([1, 2, 3, 4].values(), "☂"), "1☂2☂3☂4");
+  });
+
+  it('[[Call]] uses "," if no separator is provided', () => {
+    assertStrictEquals(join([1, 2, 3, 4].values()), "1,2,3,4");
+  });
+
+  it("[[Call]] uses the empty sting for nullish values", () => {
+    assertStrictEquals(
+      join([null, , null, undefined].values(), "☂"),
+      "☂☂☂",
+    );
+  });
+
+  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("scalarValues", () => {
+  it("[[Call]] returns an iterable", () => {
+    assertStrictEquals(
+      typeof scalarValues("")[Symbol.iterator],
+      "function",
+    );
+  });
+
+  it("[[Call]] returns an iterator", () => {
+    assertStrictEquals(typeof scalarValues("").next, "function");
+  });
+
+  it("[[Call]] returns a string scalar value iterator", () => {
+    assertStrictEquals(
+      scalarValues("")[Symbol.toStringTag],
+      "String Scalar Value Iterator",
+    );
+  });
+
+  it("[[Call]] iterates over the scalar values", () => {
+    assertEquals([
+      ...scalarValues("Ii🎙\uDFFF\uDD96\uD83C\uD800🆗☺"),
+    ], [
+      0x49,
+      0x69,
+      0x1F399,
+      0xFFFD,
+      0xFFFD,
+      0xFFFD,
+      0xFFFD,
+      0x1F197,
+      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", () => {
+  it("[[Call]] splits on sequences of spaces", () => {
+    assertEquals(
+      splitOnAsciiWhitespace("🅰️   🅱️ 🆎  🅾️"),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+  });
+
+  it("[[Call]] splits on sequences of tabs", () => {
+    assertEquals(
+      splitOnAsciiWhitespace("🅰️\t\t\t🅱️\t🆎\t\t🅾️"),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+  });
+
+  it("[[Call]] splits on sequences of carriage returns", () => {
+    assertEquals(
+      splitOnAsciiWhitespace("🅰️\r\r\r🅱️\r🆎\r\r🅾️"),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+  });
+
+  it("[[Call]] splits on sequences of newlines", () => {
+    assertEquals(
+      splitOnAsciiWhitespace("🅰️\r\r\r🅱️\r🆎\r\r🅾️"),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+  });
+
+  it("[[Call]] splits on sequences of form feeds", () => {
+    assertEquals(
+      splitOnAsciiWhitespace("🅰️\f\f\f🅱️\f🆎\f\f🅾️"),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+  });
+
+  it("[[Call]] splits on mixed whitespace", () => {
+    assertEquals(
+      splitOnAsciiWhitespace("🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️"),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+  });
+
+  it("[[Call]] returns an array of just the empty string for the empty string", () => {
+    assertEquals(splitOnAsciiWhitespace(""), [""]);
+  });
+
+  it("[[Call]] returns a single token if there are no spaces", () => {
+    assertEquals(splitOnAsciiWhitespace("abcd"), ["abcd"]);
+  });
+
+  it("[[Call]] does not split on other kinds of whitespace", () => {
+    assertEquals(
+      splitOnAsciiWhitespace("a\u202F\u205F\xa0\v\0\bb"),
+      ["a\u202F\u205F\xa0\v\0\bb"],
+    );
+  });
+
+  it("[[Call]] trims leading and trailing whitespace", () => {
+    assertEquals(
+      splitOnAsciiWhitespace(
+        "\f\r\n\r\n \n\t🅰️\f \t\n🅱️\r🆎\n\f🅾️\n\f",
+      ),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+  });
+
+  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", () => {
+  it("[[Call]] splits on commas", () => {
+    assertEquals(
+      splitOnCommas("🅰️,🅱️,🆎,🅾️"),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+  });
+
+  it("[[Call]] returns an array of just the empty string for the empty string", () => {
+    assertEquals(splitOnCommas(""), [""]);
+  });
+
+  it("[[Call]] returns a single token if there are no commas", () => {
     assertEquals(splitOnCommas("abcd"), ["abcd"]);
   });
 
-  it("[[Call]] splits into empty strings if there are only commas", () => {
-    assertEquals(splitOnCommas(",,,"), ["", "", "", ""]);
+  it("[[Call]] splits into empty strings if there are only commas", () => {
+    assertEquals(splitOnCommas(",,,"), ["", "", "", ""]);
+  });
+
+  it("[[Call]] trims leading and trailing whitespace", () => {
+    assertEquals(
+      splitOnCommas("\f\r\n\r\n \n\t🅰️,🅱️,🆎,🅾️\n\f"),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+    assertEquals(
+      splitOnCommas("\f\r\n\r\n \n\t,,,\n\f"),
+      ["", "", "", ""],
+    );
+  });
+
+  it("[[Call]] removes whitespace from the split tokens", () => {
+    assertEquals(
+      splitOnCommas(
+        "\f\r\n\r\n \n\t🅰️\f , \t\n🅱️,\r\n\r🆎\n\f,🅾️\n\f",
+      ),
+      ["🅰️", "🅱️", "🆎", "🅾️"],
+    );
+    assertEquals(
+      splitOnCommas("\f\r\n\r\n \n\t\f , \t\n,\r\n\r\n\f,\n\f"),
+      ["", "", "", ""],
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new splitOnCommas(""));
+  });
+
+  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");
+    });
   });
+});
 
-  it("[[Call]] trims leading and trailing whitespace", () => {
+describe("stringMatchAll", () => {
+  it("[[Call]] does the match akin to String::matchAll", () => {
     assertEquals(
-      splitOnCommas("\f\r\n\r\n \n\t🅰️,🅱️,🆎,🅾️\n\f"),
-      ["🅰️", "🅱️", "🆎", "🅾️"],
+      [...stringMatchAll("very success full", /([sc]+)[ue]?/g)].map((
+        match,
+      ) => [...match]),
+      [["su", "s"], ["cce", "cc"], ["ss", "ss"]],
     );
-    assertEquals(
-      splitOnCommas("\f\r\n\r\n \n\t,,,\n\f"),
-      ["", "", "", ""],
+  });
+
+  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("[[Call]] removes whitespace from the split tokens", () => {
-    assertEquals(
-      splitOnCommas(
-        "\f\r\n\r\n \n\t🅰️\f , \t\n🅱️,\r\n\r🆎\n\f,🅾️\n\f",
+  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",
     );
-    assertEquals(
-      splitOnCommas("\f\r\n\r\n \n\t\f , \t\n,\r\n\r\n\f,\n\f"),
-      ["", "", "", ""],
+  });
+
+  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", () => {
+describe("stripAndCollapseAsciiWhitespace", () => {
   it("[[Call]] collapses mixed inner whitespace", () => {
     assertEquals(
-      stripAndCollapseASCIIWhitespace("🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️"),
+      stripAndCollapseAsciiWhitespace("🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️"),
       "🅰️ 🅱️ 🆎 🅾️",
     );
   });
 
   it("[[Call]] trims leading and trailing whitespace", () => {
     assertStrictEquals(
-      stripAndCollapseASCIIWhitespace(
+      stripAndCollapseAsciiWhitespace(
         "\f\r\n\r\n \n\t\f 🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️\n\f",
       ),
       "🅰️ 🅱️ 🆎 🅾️",
@@ -559,23 +1959,42 @@ describe("stripAndCollapseASCIIWhitespace", () => {
 
   it("[[Call]] returns the empty string for strings of whitespace", () => {
     assertStrictEquals(
-      stripAndCollapseASCIIWhitespace("\f\r\n\r\n \n\t\f \n\f"),
+      stripAndCollapseAsciiWhitespace("\f\r\n\r\n \n\t\f \n\f"),
       "",
     );
   });
 
   it("[[Call]] does not collapse other kinds of whitespace", () => {
     assertEquals(
-      stripAndCollapseASCIIWhitespace("a\u202F\u205F\xa0\v\0\bb"),
+      stripAndCollapseAsciiWhitespace("a\u202F\u205F\xa0\v\0\bb"),
       "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", () => {
+describe("stripLeadingAndTrailingAsciiWhitespace", () => {
   it("[[Call]] trims leading and trailing whitespace", () => {
     assertStrictEquals(
-      stripLeadingAndTrailingASCIIWhitespace(
+      stripLeadingAndTrailingAsciiWhitespace(
         "\f\r\n\r\n \n\t\f 🅰️🅱️🆎🅾️\n\f",
       ),
       "🅰️🅱️🆎🅾️",
@@ -584,14 +2003,14 @@ describe("stripLeadingAndTrailingASCIIWhitespace", () => {
 
   it("[[Call]] returns the empty string for strings of whitespace", () => {
     assertStrictEquals(
-      stripLeadingAndTrailingASCIIWhitespace("\f\r\n\r\n \n\t\f \n\f"),
+      stripLeadingAndTrailingAsciiWhitespace("\f\r\n\r\n \n\t\f \n\f"),
       "",
     );
   });
 
   it("[[Call]] does not trim other kinds of whitespace", () => {
     assertEquals(
-      stripLeadingAndTrailingASCIIWhitespace(
+      stripLeadingAndTrailingAsciiWhitespace(
         "\v\u202F\u205Fx\0\b\xa0",
       ),
       "\v\u202F\u205Fx\0\b\xa0",
@@ -600,10 +2019,89 @@ describe("stripLeadingAndTrailingASCIIWhitespace", () => {
 
   it("[[Call]] does not adjust inner whitespace", () => {
     assertEquals(
-      stripLeadingAndTrailingASCIIWhitespace("a   b"),
+      stripLeadingAndTrailingAsciiWhitespace("a   b"),
       "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("toScalarValueString", () => {
+  it("[[Call]] replaces invalid values", () => {
+    assertStrictEquals(
+      toScalarValueString("Ii🎙\uDFFF\uDD96\uD83C\uD800🆗☺"),
+      "Ii🎙\uFFFD\uFFFD\uFFFD\uFFFD🆗☺",
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new toScalarValueString(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(toScalarValueString.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        toScalarValueString.name,
+        "toScalarValueString",
+      );
+    });
+  });
 });
 
 describe("toString", () => {
@@ -621,4 +2119,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.091338 seconds and 4 git commands to generate.