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
, 
  34 describe("Matcher", () => { 
  35   it("[[Construct]] accepts a string first argument", () => { 
  36     assert(new Matcher("")); 
  39   it("[[Construct]] accepts a unicode regular expression first argument", () => { 
  40     assert(new Matcher(/(?:)/u)); 
  43   it("[[Construct]] throws with a nonยทunicode regular expression first argument", () => { 
  44     assertThrows(() => new Matcher(/(?:)/)); 
  47   it("[[Construct]] creates a callable object", () => { 
  48     assertStrictEquals(typeof new Matcher(""), "function"); 
  51   it("[[Construct]] creates a new Matcher", () => { 
  53       Object
.getPrototypeOf(new Matcher("")), 
  58   it("[[Construct]] creates an object which inherits from RegExp", () => { 
  59     assert(new Matcher("") instanceof RegExp
); 
  62   describe("::dotAll", () => { 
  63     it("[[Get]] returns true when the dotAll flag is present", () => { 
  64       assertStrictEquals(new Matcher(/(?:)/su).dotAll
, true); 
  67     it("[[Get]] returns false when the dotAll flag is not present", () => { 
  68       assertStrictEquals(new Matcher(/(?:)/u).dotAll
, false); 
  72   describe("::exec", () => { 
  73     it("[[Call]] returns the match object given a complete match", () => { 
  75         [...new Matcher(/.(?<wow>(?:.(?=.))*)(.)?/u).exec("success")], 
  76         ["success", "ucces", "s"], 
  80     it("[[Call]] returns null given a partial match", () => { 
  81       assertEquals(new Matcher("").exec("failure"), null); 
  85   describe("::global", () => { 
  86     it("[[Get]] returns true when the global flag is present", () => { 
  87       assertStrictEquals(new Matcher(/(?:)/gu).global
, true); 
  90     it("[[Get]] returns false when the global flag is not present", () => { 
  91       assertStrictEquals(new Matcher(/(?:)/u).global
, false); 
  95   describe("::hasIndices", () => { 
  96     it("[[Get]] returns true when the hasIndices flag is present", () => { 
  97       assertStrictEquals(new Matcher(/(?:)/du).hasIndices
, true); 
 100     it("[[Get]] returns false when the hasIndices flag is not present", () => { 
 101       assertStrictEquals(new Matcher(/(?:)/u).hasIndices
, false); 
 105   describe("::ignoreCase", () => { 
 106     it("[[Get]] returns true when the ignoreCase flag is present", () => { 
 107       assertStrictEquals(new Matcher(/(?:)/iu).ignoreCase
, true); 
 110     it("[[Get]] returns false when the ignoreCase flag is not present", () => { 
 111       assertStrictEquals(new Matcher(/(?:)/u).ignoreCase
, false); 
 115   describe("::multiline", () => { 
 116     it("[[Get]] returns true when the multiline flag is present", () => { 
 117       assertStrictEquals(new Matcher(/(?:)/mu).multiline
, true); 
 120     it("[[Get]] returns false when the multiline flag is not present", () => { 
 121       assertStrictEquals(new Matcher(/(?:)/u).multiline
, false); 
 125   describe("::source", () => { 
 126     it("[[Get]] returns the RegExp source", () => { 
 127       assertStrictEquals(new Matcher("").source
, "(?:)"); 
 128       assertStrictEquals(new Matcher(/.*/su).source
, ".*"); 
 132   describe("::sticky", () => { 
 133     it("[[Get]] returns true when the sticky flag is present", () => { 
 134       assertStrictEquals(new Matcher(/(?:)/uy).sticky
, true); 
 137     it("[[Get]] returns false when the sticky flag is not present", () => { 
 138       assertStrictEquals(new Matcher(/(?:)/u).sticky
, false); 
 142   describe("::unicode", () => { 
 143     it("[[Get]] returns true when the unicode flag is present", () => { 
 144       assertStrictEquals(new Matcher(/(?:)/u).unicode
, true); 
 148   describe("~", () => { 
 149     it("[[Call]] returns true for a complete match", () => { 
 150       assertStrictEquals(new Matcher("")(""), true); 
 151       assertStrictEquals(new Matcher(/.*/su)("success\nyay"), true); 
 154     it("[[Call]] returns false for a partial match", () => { 
 155       assertStrictEquals(new Matcher("")("failure"), false); 
 156       assertStrictEquals(new Matcher(/.*/u)("failure\nno"), false); 
 160   describe("~lastIndex", () => { 
 161     it("[[Get]] returns zero", () => { 
 162       assertStrictEquals(new Matcher("").lastIndex
, 0); 
 165     it("[[Set]] fails", () => { 
 166       assertThrows(() => (new Matcher("").lastIndex 
= 1)); 
 170   describe("~length", () => { 
 171     it("[[Get]] returns one", () => { 
 172       assertStrictEquals(new Matcher("").length
, 1); 
 176   describe("~name", () => { 
 177     it("[[Get]] wraps the stringified regular expression if no name was provided", () => { 
 178       assertStrictEquals(new Matcher("").name
, "Matcher(/(?:)/u)"); 
 180         new Matcher(/.*/gsu).name
, 
 185     it("[[Get]] uses the provided name if one was provided", () => { 
 186       assertStrictEquals(new Matcher("", "success").name
, "success"); 
 191 describe("asciiLowercase", () => { 
 192   it("[[Call]] lowercases (just) AยทSยทCยทIยทI letters", () => { 
 193     assertStrictEquals(asciiLowercase("aBลฟรss Ftษษร"), "abลฟรss ftษษร"); 
 197 describe("asciiUppercase", () => { 
 198   it("[[Call]] uppercases (just) AยทSยทCยทIยทI letters", () => { 
 199     assertStrictEquals(asciiUppercase("aBลฟรss Ftษษร"), "ABลฟรSS FTษษร"); 
 203 describe("codeUnits", () => { 
 204   it("[[Call]] returns an iterable", () => { 
 206       typeof codeUnits("")[Symbol
.iterator
], 
 211   it("[[Call]] returns an iterator", () => { 
 212     assertStrictEquals(typeof codeUnits("").next
, "function"); 
 215   it("[[Call]] returns a string code value iterator", () => { 
 217       codeUnits("")[Symbol
.toStringTag
], 
 218       "String Code Value Iterator", 
 222   it("[[Call]] iterates over the code units", () => { 
 224       ...codeUnits("Ii๐\uDFFF\uDD96\uD83C\uD800๐โบ"), 
 241 describe("codepoints", () => { 
 242   it("[[Call]] returns an iterable", () => { 
 244       typeof codepoints("")[Symbol
.iterator
], 
 249   it("[[Call]] returns an iterator", () => { 
 250     assertStrictEquals(typeof codepoints("").next
, "function"); 
 253   it("[[Call]] returns a string code value iterator", () => { 
 255       codepoints("")[Symbol
.toStringTag
], 
 256       "String Code Value Iterator", 
 260   it("[[Call]] iterates over the codepoints", () => { 
 262       ...codepoints("Ii๐\uDFFF\uDD96\uD83C\uD800๐โบ"), 
 277 describe("getCharacter", () => { 
 278   it("[[Call]] returns the character at the provided position", () => { 
 279     assertStrictEquals(getCharacter("Ii๐๐โบ", 4), "๐"); 
 282   it("[[Call]] returns a low surrogate if the provided position splits a character", () => { 
 283     assertStrictEquals(getCharacter("Ii๐๐โบ", 5), "\uDD97"); 
 286   it("[[Call]] returns undefined for an outโofโbounds index", () => { 
 287     assertStrictEquals(getCharacter("Ii๐๐โบ", -1), void {}); 
 288     assertStrictEquals(getCharacter("Ii๐๐โบ", 7), void {}); 
 292 describe("join", () => { 
 293   it("[[Call]] joins the provided iterator with the provided separartor", () => { 
 294     assertStrictEquals(join([1, 2, 3, 4].values(), "โ"), "1โ2โ3โ4"); 
 297   it('[[Call]] uses "," if no separator is provided', () => { 
 298     assertStrictEquals(join([1, 2, 3, 4].values()), "1,2,3,4"); 
 301   it("[[Call]] uses the empty sting for nullish values", () => { 
 303       join([null, , null, undefined].values(), "โ"), 
 309 describe("scalarValueString", () => { 
 310   it("[[Call]] replaces invalid values", () => { 
 312       scalarValueString("Ii๐\uDFFF\uDD96\uD83C\uD800๐โบ"), 
 313       "Ii๐\uFFFD\uFFFD\uFFFD\uFFFD๐โบ", 
 318 describe("scalarValues", () => { 
 319   it("[[Call]] returns an iterable", () => { 
 321       typeof scalarValues("")[Symbol
.iterator
], 
 326   it("[[Call]] returns an iterator", () => { 
 327     assertStrictEquals(typeof scalarValues("").next
, "function"); 
 330   it("[[Call]] returns a string code value iterator", () => { 
 332       scalarValues("")[Symbol
.toStringTag
], 
 333       "String Code Value Iterator", 
 337   it("[[Call]] iterates over the scalar values", () => { 
 339       ...scalarValues("Ii๐\uDFFF\uDD96\uD83C\uD800๐โบ"), 
 354 describe("splitOnASCIIWhitespace", () => { 
 355   it("[[Call]] splits on sequences of spaces", () => { 
 357       splitOnASCIIWhitespace("๐
ฐ๏ธ   ๐
ฑ๏ธ ๐  ๐
พ๏ธ"), 
 358       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 362   it("[[Call]] splits on sequences of tabs", () => { 
 364       splitOnASCIIWhitespace("๐
ฐ๏ธ\t\t\t๐
ฑ๏ธ\t๐\t\t๐
พ๏ธ"), 
 365       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 369   it("[[Call]] splits on sequences of carriage returns", () => { 
 371       splitOnASCIIWhitespace("๐
ฐ๏ธ\r\r\r๐
ฑ๏ธ\r๐\r\r๐
พ๏ธ"), 
 372       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 376   it("[[Call]] splits on sequences of newlines", () => { 
 378       splitOnASCIIWhitespace("๐
ฐ๏ธ\r\r\r๐
ฑ๏ธ\r๐\r\r๐
พ๏ธ"), 
 379       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 383   it("[[Call]] splits on sequences of form feeds", () => { 
 385       splitOnASCIIWhitespace("๐
ฐ๏ธ\f\f\f๐
ฑ๏ธ\f๐\f\f๐
พ๏ธ"), 
 386       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 390   it("[[Call]] splits on mixed whitespace", () => { 
 392       splitOnASCIIWhitespace("๐
ฐ๏ธ\f \t\n๐
ฑ๏ธ\r\n\r๐\n\f๐
พ๏ธ"), 
 393       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 397   it("[[Call]] returns an array of just the empty string for the empty string", () => { 
 398     assertEquals(splitOnASCIIWhitespace(""), [""]); 
 401   it("[[Call]] returns a single token if there are no spaces", () => { 
 402     assertEquals(splitOnASCIIWhitespace("abcd"), ["abcd"]); 
 405   it("[[Call]] does not split on other kinds of whitespace", () => { 
 407       splitOnASCIIWhitespace("a\u202F\u205F\xa0\v\0\bb"), 
 408       ["a\u202F\u205F\xa0\v\0\bb"], 
 412   it("[[Call]] trims leading and trailing whitespace", () => { 
 414       splitOnASCIIWhitespace( 
 415         "\f\r\n\r\n \n\t๐
ฐ๏ธ\f \t\n๐
ฑ๏ธ\r๐\n\f๐
พ๏ธ\n\f", 
 417       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 422 describe("splitOnCommas", () => { 
 423   it("[[Call]] splits on commas", () => { 
 425       splitOnCommas("๐
ฐ๏ธ,๐
ฑ๏ธ,๐,๐
พ๏ธ"), 
 426       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 430   it("[[Call]] returns an array of just the empty string for the empty string", () => { 
 431     assertEquals(splitOnCommas(""), [""]); 
 434   it("[[Call]] returns a single token if there are no commas", () => { 
 435     assertEquals(splitOnCommas("abcd"), ["abcd"]); 
 438   it("[[Call]] splits into empty strings if there are only commas", () => { 
 439     assertEquals(splitOnCommas(",,,"), ["", "", "", ""]); 
 442   it("[[Call]] trims leading and trailing whitespace", () => { 
 444       splitOnCommas("\f\r\n\r\n \n\t๐
ฐ๏ธ,๐
ฑ๏ธ,๐,๐
พ๏ธ\n\f"), 
 445       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 448       splitOnCommas("\f\r\n\r\n \n\t,,,\n\f"), 
 453   it("[[Call]] removes whitespace from the split tokens", () => { 
 456         "\f\r\n\r\n \n\t๐
ฐ๏ธ\f , \t\n๐
ฑ๏ธ,\r\n\r๐\n\f,๐
พ๏ธ\n\f", 
 458       ["๐
ฐ๏ธ", "๐
ฑ๏ธ", "๐", "๐
พ๏ธ"], 
 461       splitOnCommas("\f\r\n\r\n \n\t\f , \t\n,\r\n\r\n\f,\n\f"), 
 467 describe("stripAndCollapseASCIIWhitespace", () => { 
 468   it("[[Call]] collapses mixed inner whitespace", () => { 
 470       stripAndCollapseASCIIWhitespace("๐
ฐ๏ธ\f \t\n๐
ฑ๏ธ\r\n\r๐\n\f๐
พ๏ธ"), 
 471       "๐
ฐ๏ธ ๐
ฑ๏ธ ๐ ๐
พ๏ธ", 
 475   it("[[Call]] trims leading and trailing whitespace", () => { 
 477       stripAndCollapseASCIIWhitespace( 
 478         "\f\r\n\r\n \n\t\f ๐
ฐ๏ธ\f \t\n๐
ฑ๏ธ\r\n\r๐\n\f๐
พ๏ธ\n\f", 
 480       "๐
ฐ๏ธ ๐
ฑ๏ธ ๐ ๐
พ๏ธ", 
 484   it("[[Call]] returns the empty string for strings of whitespace", () => { 
 486       stripAndCollapseASCIIWhitespace("\f\r\n\r\n \n\t\f \n\f"), 
 491   it("[[Call]] does not collapse other kinds of whitespace", () => { 
 493       stripAndCollapseASCIIWhitespace("a\u202F\u205F\xa0\v\0\bb"), 
 494       "a\u202F\u205F\xa0\v\0\bb", 
 499 describe("stripLeadingAndTrailingASCIIWhitespace", () => { 
 500   it("[[Call]] trims leading and trailing whitespace", () => { 
 502       stripLeadingAndTrailingASCIIWhitespace( 
 503         "\f\r\n\r\n \n\t\f ๐
ฐ๏ธ๐
ฑ๏ธ๐๐
พ๏ธ\n\f", 
 505       "๐
ฐ๏ธ๐
ฑ๏ธ๐๐
พ๏ธ", 
 509   it("[[Call]] returns the empty string for strings of whitespace", () => { 
 511       stripLeadingAndTrailingASCIIWhitespace("\f\r\n\r\n \n\t\f \n\f"), 
 516   it("[[Call]] does not trim other kinds of whitespace", () => { 
 518       stripLeadingAndTrailingASCIIWhitespace( 
 519         "\v\u202F\u205Fx\0\b\xa0", 
 521       "\v\u202F\u205Fx\0\b\xa0", 
 525   it("[[Call]] does not adjust inner whitespace", () => { 
 527       stripLeadingAndTrailingASCIIWhitespace("a   b"),