1 // โ๐ Piscฤs โท string.test.js
2 // ====================================================================
4 // Copyright ยฉ 2022 Lady [@ Ladyโs Computer].
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
17 } from "./dev-deps.js";
28 splitOnASCIIWhitespace
,
30 stripAndCollapseASCIIWhitespace
,
31 stripLeadingAndTrailingASCIIWhitespace
,
35 describe("Matcher", () => {
36 it("[[Construct]] accepts a string first argument", () => {
37 assert(new Matcher(""));
40 it("[[Construct]] accepts a unicode regular expression first argument", () => {
41 assert(new Matcher(/(?:)/u));
44 it("[[Construct]] throws with a nonยทunicode regular expression first argument", () => {
45 assertThrows(() => new Matcher(/(?:)/));
48 it("[[Construct]] creates a callable object", () => {
49 assertStrictEquals(typeof new Matcher(""), "function");
52 it("[[Construct]] creates a new Matcher", () => {
54 Object
.getPrototypeOf(new Matcher("")),
59 it("[[Construct]] creates an object which inherits from RegExp", () => {
60 assert(new Matcher("") instanceof RegExp
);
63 describe("::dotAll", () => {
64 it("[[Get]] returns true when the dotAll flag is present", () => {
65 assertStrictEquals(new Matcher(/(?:)/su).dotAll
, true);
68 it("[[Get]] returns false when the dotAll flag is not present", () => {
69 assertStrictEquals(new Matcher(/(?:)/u).dotAll
, false);
73 describe("::exec", () => {
74 it("[[Call]] returns the match object given a complete match", () => {
76 [...new Matcher(/.(?<wow>(?:.(?=.))*)(.)?/u).exec("success")],
77 ["success", "ucces", "s"],
81 it("[[Call]] returns null given a partial match", () => {
82 assertEquals(new Matcher("").exec("failure"), null);
86 describe("::global", () => {
87 it("[[Get]] returns true when the global flag is present", () => {
88 assertStrictEquals(new Matcher(/(?:)/gu).global
, true);
91 it("[[Get]] returns false when the global flag is not present", () => {
92 assertStrictEquals(new Matcher(/(?:)/u).global
, false);
96 describe("::hasIndices", () => {
97 it("[[Get]] returns true when the hasIndices flag is present", () => {
98 assertStrictEquals(new Matcher(/(?:)/du).hasIndices
, true);
101 it("[[Get]] returns false when the hasIndices flag is not present", () => {
102 assertStrictEquals(new Matcher(/(?:)/u).hasIndices
, false);
106 describe("::ignoreCase", () => {
107 it("[[Get]] returns true when the ignoreCase flag is present", () => {
108 assertStrictEquals(new Matcher(/(?:)/iu).ignoreCase
, true);
111 it("[[Get]] returns false when the ignoreCase flag is not present", () => {
112 assertStrictEquals(new Matcher(/(?:)/u).ignoreCase
, false);
116 describe("::multiline", () => {
117 it("[[Get]] returns true when the multiline flag is present", () => {
118 assertStrictEquals(new Matcher(/(?:)/mu).multiline
, true);
121 it("[[Get]] returns false when the multiline flag is not present", () => {
122 assertStrictEquals(new Matcher(/(?:)/u).multiline
, false);
126 describe("::source", () => {
127 it("[[Get]] returns the RegExp source", () => {
128 assertStrictEquals(new Matcher("").source
, "(?:)");
129 assertStrictEquals(new Matcher(/.*/su).source
, ".*");
133 describe("::sticky", () => {
134 it("[[Get]] returns true when the sticky flag is present", () => {
135 assertStrictEquals(new Matcher(/(?:)/uy).sticky
, true);
138 it("[[Get]] returns false when the sticky flag is not present", () => {
139 assertStrictEquals(new Matcher(/(?:)/u).sticky
, false);
143 describe("::unicode", () => {
144 it("[[Get]] returns true when the unicode flag is present", () => {
145 assertStrictEquals(new Matcher(/(?:)/u).unicode
, true);
149 describe("~", () => {
150 it("[[Call]] returns true for a complete match", () => {
151 assertStrictEquals(new Matcher("")(""), true);
152 assertStrictEquals(new Matcher(/.*/su)("success\nyay"), true);
155 it("[[Call]] returns false for a partial match", () => {
156 assertStrictEquals(new Matcher("")("failure"), false);
157 assertStrictEquals(new Matcher(/.*/u)("failure\nno"), false);
161 describe("~lastIndex", () => {
162 it("[[Get]] returns zero", () => {
163 assertStrictEquals(new Matcher("").lastIndex
, 0);
166 it("[[Set]] fails", () => {
167 assertThrows(() => (new Matcher("").lastIndex
= 1));
171 describe("~length", () => {
172 it("[[Get]] returns one", () => {
173 assertStrictEquals(new Matcher("").length
, 1);
177 describe("~name", () => {
178 it("[[Get]] wraps the stringified regular expression if no name was provided", () => {
179 assertStrictEquals(new Matcher("").name
, "Matcher(/(?:)/u)");
181 new Matcher(/.*/gsu).name
,
186 it("[[Get]] uses the provided name if one was provided", () => {
187 assertStrictEquals(new Matcher("", "success").name
, "success");
192 describe("asciiLowercase", () => {
193 it("[[Call]] lowercases (just) AยทSยทCยทIยทI letters", () => {
194 assertStrictEquals(asciiLowercase("aBลฟรss Ftษษร"), "abลฟรss ftษษร");
198 describe("asciiUppercase", () => {
199 it("[[Call]] uppercases (just) AยทSยทCยทIยทI letters", () => {
200 assertStrictEquals(asciiUppercase("aBลฟรss Ftษษร"), "ABลฟรSS FTษษร");
204 describe("codeUnits", () => {
205 it("[[Call]] returns an iterable", () => {
207 typeof codeUnits("")[Symbol
.iterator
],
212 it("[[Call]] returns an iterator", () => {
213 assertStrictEquals(typeof codeUnits("").next
, "function");
216 it("[[Call]] returns a string code value iterator", () => {
218 codeUnits("")[Symbol
.toStringTag
],
219 "String Code Value Iterator",
223 it("[[Call]] iterates over the code units", () => {
225 ...codeUnits("Ii๐\uDFFF\uDD96\uD83C\uD800๐โบ"),
242 describe("codepoints", () => {
243 it("[[Call]] returns an iterable", () => {
245 typeof codepoints("")[Symbol
.iterator
],
250 it("[[Call]] returns an iterator", () => {
251 assertStrictEquals(typeof codepoints("").next
, "function");
254 it("[[Call]] returns a string code value iterator", () => {
256 codepoints("")[Symbol
.toStringTag
],
257 "String Code Value Iterator",
261 it("[[Call]] iterates over the codepoints", () => {
263 ...codepoints("Ii๐\uDFFF\uDD96\uD83C\uD800๐โบ"),
278 describe("getCharacter", () => {
279 it("[[Call]] returns the character at the provided position", () => {
280 assertStrictEquals(getCharacter("Ii๐๐โบ", 4), "๐");
283 it("[[Call]] returns a low surrogate if the provided position splits a character", () => {
284 assertStrictEquals(getCharacter("Ii๐๐โบ", 5), "\uDD97");
287 it("[[Call]] returns undefined for an outโofโbounds index", () => {
288 assertStrictEquals(getCharacter("Ii๐๐โบ", -1), void {});
289 assertStrictEquals(getCharacter("Ii๐๐โบ", 7), void {});
293 describe("join", () => {
294 it("[[Call]] joins the provided iterator with the provided separartor", () => {
295 assertStrictEquals(join([1, 2, 3, 4].values(), "โ"), "1โ2โ3โ4");
298 it('[[Call]] uses "," if no separator is provided', () => {
299 assertStrictEquals(join([1, 2, 3, 4].values()), "1,2,3,4");
302 it("[[Call]] uses the empty sting for nullish values", () => {
304 join([null, , null, undefined].values(), "โ"),
310 describe("scalarValueString", () => {
311 it("[[Call]] replaces invalid values", () => {
313 scalarValueString("Ii๐\uDFFF\uDD96\uD83C\uD800๐โบ"),
314 "Ii๐\uFFFD\uFFFD\uFFFD\uFFFD๐โบ",
319 describe("scalarValues", () => {
320 it("[[Call]] returns an iterable", () => {
322 typeof scalarValues("")[Symbol
.iterator
],
327 it("[[Call]] returns an iterator", () => {
328 assertStrictEquals(typeof scalarValues("").next
, "function");
331 it("[[Call]] returns a string code value iterator", () => {
333 scalarValues("")[Symbol
.toStringTag
],
334 "String Code Value Iterator",
338 it("[[Call]] iterates over the scalar values", () => {
340 ...scalarValues("Ii๐\uDFFF\uDD96\uD83C\uD800๐โบ"),
355 describe("splitOnASCIIWhitespace", () => {
356 it("[[Call]] splits on sequences of spaces", () => {
358 splitOnASCIIWhitespace("๐
ฐ๏ธ ๐
ฑ๏ธ ๐ ๐
พ๏ธ"),
359 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
363 it("[[Call]] splits on sequences of tabs", () => {
365 splitOnASCIIWhitespace("๐
ฐ๏ธ\t\t\t๐
ฑ๏ธ\t๐\t\t๐
พ๏ธ"),
366 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
370 it("[[Call]] splits on sequences of carriage returns", () => {
372 splitOnASCIIWhitespace("๐
ฐ๏ธ\r\r\r๐
ฑ๏ธ\r๐\r\r๐
พ๏ธ"),
373 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
377 it("[[Call]] splits on sequences of newlines", () => {
379 splitOnASCIIWhitespace("๐
ฐ๏ธ\r\r\r๐
ฑ๏ธ\r๐\r\r๐
พ๏ธ"),
380 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
384 it("[[Call]] splits on sequences of form feeds", () => {
386 splitOnASCIIWhitespace("๐
ฐ๏ธ\f\f\f๐
ฑ๏ธ\f๐\f\f๐
พ๏ธ"),
387 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
391 it("[[Call]] splits on mixed whitespace", () => {
393 splitOnASCIIWhitespace("๐
ฐ๏ธ\f \t\n๐
ฑ๏ธ\r\n\r๐\n\f๐
พ๏ธ"),
394 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
398 it("[[Call]] returns an array of just the empty string for the empty string", () => {
399 assertEquals(splitOnASCIIWhitespace(""), [""]);
402 it("[[Call]] returns a single token if there are no spaces", () => {
403 assertEquals(splitOnASCIIWhitespace("abcd"), ["abcd"]);
406 it("[[Call]] does not split on other kinds of whitespace", () => {
408 splitOnASCIIWhitespace("a\u202F\u205F\xa0\v\0\bb"),
409 ["a\u202F\u205F\xa0\v\0\bb"],
413 it("[[Call]] trims leading and trailing whitespace", () => {
415 splitOnASCIIWhitespace(
416 "\f\r\n\r\n \n\t๐
ฐ๏ธ\f \t\n๐
ฑ๏ธ\r๐\n\f๐
พ๏ธ\n\f",
418 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
423 describe("splitOnCommas", () => {
424 it("[[Call]] splits on commas", () => {
426 splitOnCommas("๐
ฐ๏ธ,๐
ฑ๏ธ,๐,๐
พ๏ธ"),
427 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
431 it("[[Call]] returns an array of just the empty string for the empty string", () => {
432 assertEquals(splitOnCommas(""), [""]);
435 it("[[Call]] returns a single token if there are no commas", () => {
436 assertEquals(splitOnCommas("abcd"), ["abcd"]);
439 it("[[Call]] splits into empty strings if there are only commas", () => {
440 assertEquals(splitOnCommas(",,,"), ["", "", "", ""]);
443 it("[[Call]] trims leading and trailing whitespace", () => {
445 splitOnCommas("\f\r\n\r\n \n\t๐
ฐ๏ธ,๐
ฑ๏ธ,๐,๐
พ๏ธ\n\f"),
446 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
449 splitOnCommas("\f\r\n\r\n \n\t,,,\n\f"),
454 it("[[Call]] removes whitespace from the split tokens", () => {
457 "\f\r\n\r\n \n\t๐
ฐ๏ธ\f , \t\n๐
ฑ๏ธ,\r\n\r๐\n\f,๐
พ๏ธ\n\f",
459 ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"],
462 splitOnCommas("\f\r\n\r\n \n\t\f , \t\n,\r\n\r\n\f,\n\f"),
468 describe("stripAndCollapseASCIIWhitespace", () => {
469 it("[[Call]] collapses mixed inner whitespace", () => {
471 stripAndCollapseASCIIWhitespace("๐
ฐ๏ธ\f \t\n๐
ฑ๏ธ\r\n\r๐\n\f๐
พ๏ธ"),
472 "๐
ฐ๏ธ ๐
ฑ๏ธ ๐ ๐
พ๏ธ",
476 it("[[Call]] trims leading and trailing whitespace", () => {
478 stripAndCollapseASCIIWhitespace(
479 "\f\r\n\r\n \n\t\f ๐
ฐ๏ธ\f \t\n๐
ฑ๏ธ\r\n\r๐\n\f๐
พ๏ธ\n\f",
481 "๐
ฐ๏ธ ๐
ฑ๏ธ ๐ ๐
พ๏ธ",
485 it("[[Call]] returns the empty string for strings of whitespace", () => {
487 stripAndCollapseASCIIWhitespace("\f\r\n\r\n \n\t\f \n\f"),
492 it("[[Call]] does not collapse other kinds of whitespace", () => {
494 stripAndCollapseASCIIWhitespace("a\u202F\u205F\xa0\v\0\bb"),
495 "a\u202F\u205F\xa0\v\0\bb",
500 describe("stripLeadingAndTrailingASCIIWhitespace", () => {
501 it("[[Call]] trims leading and trailing whitespace", () => {
503 stripLeadingAndTrailingASCIIWhitespace(
504 "\f\r\n\r\n \n\t\f ๐
ฐ๏ธ๐
ฑ๏ธ๐๐
พ๏ธ\n\f",
506 "๐
ฐ๏ธ๐
ฑ๏ธ๐๐
พ๏ธ",
510 it("[[Call]] returns the empty string for strings of whitespace", () => {
512 stripLeadingAndTrailingASCIIWhitespace("\f\r\n\r\n \n\t\f \n\f"),
517 it("[[Call]] does not trim other kinds of whitespace", () => {
519 stripLeadingAndTrailingASCIIWhitespace(
520 "\v\u202F\u205Fx\0\b\xa0",
522 "\v\u202F\u205Fx\0\b\xa0",
526 it("[[Call]] does not adjust inner whitespace", () => {
528 stripLeadingAndTrailingASCIIWhitespace("a b"),
534 describe("toString", () => {
535 it("[[Call]] converts to a string", () => {
546 it("[[Call]] throws when provided a symbol", () => {
547 assertThrows(() => toString(Symbol()));