+// ♓🌟 Piscēs ∷ string.test.js
+// ====================================================================
+//
+// Copyright © 2022 Lady [@ Lady’s Computer].
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+
+import {
+ assertEquals,
+ assertStrictEquals,
+ describe,
+ it,
+} from "./dev-deps.js";
+import {
+ asciiLowercase,
+ asciiUppercase,
+ codepoints,
+ codeUnits,
+ getCharacter,
+ join,
+ scalarValues,
+ scalarValueString,
+ splitOnASCIIWhitespace,
+ splitOnCommas,
+ stripAndCollapseASCIIWhitespace,
+ stripLeadingAndTrailingASCIIWhitespace,
+} from "./string.js";
+
+describe("asciiLowercase", () => {
+ it("[[Call]] lowercases (just) A·S·C·I·I letters", () => {
+ assertStrictEquals(asciiLowercase("aBſÆss FtɁɂß"), "abſÆss ftɁɂß");
+ });
+});
+
+describe("asciiUppercase", () => {
+ it("[[Call]] uppercases (just) A·S·C·I·I letters", () => {
+ assertStrictEquals(asciiUppercase("aBſÆss FtɁɂß"), "ABſÆSS FTɁɂß");
+ });
+});
+
+describe("codeUnits", () => {
+ it("[[Call]] returns an iterable", () => {
+ assertStrictEquals(
+ typeof codeUnits("")[Symbol.iterator],
+ "function",
+ );
+ });
+
+ it("[[Call]] returns an iterator", () => {
+ assertStrictEquals(typeof codeUnits("").next, "function");
+ });
+
+ it("[[Call]] returns a string code value iterator", () => {
+ assertStrictEquals(
+ codeUnits("")[Symbol.toStringTag],
+ "String Code Value Iterator",
+ );
+ });
+
+ it("[[Call]] iterates over the code units", () => {
+ assertEquals([
+ ...codeUnits("Ii🎙\uDFFF\uDD96\uD83C\uD800🆗☺"),
+ ], [
+ 0x49,
+ 0x69,
+ 0xD83C,
+ 0xDF99,
+ 0xDFFF,
+ 0xDD96,
+ 0xD83C,
+ 0xD800,
+ 0xD83C,
+ 0xDD97,
+ 0x263A,
+ ]);
+ });
+});
+
+describe("codepoints", () => {
+ it("[[Call]] returns an iterable", () => {
+ assertStrictEquals(
+ typeof codepoints("")[Symbol.iterator],
+ "function",
+ );
+ });
+
+ it("[[Call]] returns an iterator", () => {
+ assertStrictEquals(typeof codepoints("").next, "function");
+ });
+
+ it("[[Call]] returns a string code value iterator", () => {
+ assertStrictEquals(
+ codepoints("")[Symbol.toStringTag],
+ "String Code Value Iterator",
+ );
+ });
+
+ it("[[Call]] iterates over the codepoints", () => {
+ assertEquals([
+ ...codepoints("Ii🎙\uDFFF\uDD96\uD83C\uD800🆗☺"),
+ ], [
+ 0x49,
+ 0x69,
+ 0x1F399,
+ 0xDFFF,
+ 0xDD96,
+ 0xD83C,
+ 0xD800,
+ 0x1F197,
+ 0x263A,
+ ]);
+ });
+});
+
+describe("getCharacter", () => {
+ it("[[Call]] returns the character at the provided position", () => {
+ assertStrictEquals(getCharacter("Ii🎙🆗☺", 4), "🆗");
+ });
+
+ it("[[Call]] returns a low surrogate if the provided position splits a character", () => {
+ assertStrictEquals(getCharacter("Ii🎙🆗☺", 5), "\uDD97");
+ });
+
+ it("[[Call]] returns undefined for an out‐of‐bounds index", () => {
+ assertStrictEquals(getCharacter("Ii🎙🆗☺", -1), void {});
+ assertStrictEquals(getCharacter("Ii🎙🆗☺", 7), void {});
+ });
+});
+
+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(), "☂"),
+ "☂☂☂",
+ );
+ });
+});
+
+describe("scalarValueString", () => {
+ it("[[Call]] replaces invalid values", () => {
+ assertStrictEquals(
+ scalarValueString("Ii🎙\uDFFF\uDD96\uD83C\uD800🆗☺"),
+ "Ii🎙\uFFFD\uFFFD\uFFFD\uFFFD🆗☺",
+ );
+ });
+});
+
+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 code value iterator", () => {
+ assertStrictEquals(
+ scalarValues("")[Symbol.toStringTag],
+ "String Code 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,
+ ]);
+ });
+});
+
+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",
+ ),
+ ["🅰️", "🅱️", "🆎", "🅾️"],
+ );
+ });
+});
+
+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]] 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"),
+ ["", "", "", ""],
+ );
+ });
+});
+
+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",
+ ),
+ "🅰️ 🅱️ 🆎 🅾️",
+ );
+ });
+
+ it("[[Call]] returns the empty string for strings of whitespace", () => {
+ assertStrictEquals(
+ 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"),
+ "a\u202F\u205F\xa0\v\0\bb",
+ );
+ });
+});
+
+describe("stripLeadingAndTrailingASCIIWhitespace", () => {
+ it("[[Call]] trims leading and trailing whitespace", () => {
+ assertStrictEquals(
+ stripLeadingAndTrailingASCIIWhitespace(
+ "\f\r\n\r\n \n\t\f 🅰️🅱️🆎🅾️\n\f",
+ ),
+ "🅰️🅱️🆎🅾️",
+ );
+ });
+
+ it("[[Call]] returns the empty string for strings of whitespace", () => {
+ assertStrictEquals(
+ stripLeadingAndTrailingASCIIWhitespace("\f\r\n\r\n \n\t\f \n\f"),
+ "",
+ );
+ });
+
+ it("[[Call]] does not trim other kinds of whitespace", () => {
+ assertEquals(
+ stripLeadingAndTrailingASCIIWhitespace(
+ "\v\u202F\u205Fx\0\b\xa0",
+ ),
+ "\v\u202F\u205Fx\0\b\xa0",
+ );
+ });
+
+ it("[[Call]] does not adjust inner whitespace", () => {
+ assertEquals(
+ stripLeadingAndTrailingASCIIWhitespace("a b"),
+ "a b",
+ );
+ });
+});