]> Ladyโ€™s Gitweb - Pisces/blob - string.test.js
0af3e323f0c252f79f8bcfa64c59b5379352fb13
[Pisces] / string.test.js
1 // โ™“๐ŸŒŸ Piscฤ“s โˆท string.test.js
2 // ====================================================================
3 //
4 // Copyright ยฉ 2022 Lady [@ Ladyโ€™s Computer].
5 //
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/>.
9
10 import {
11 assert,
12 assertEquals,
13 assertStrictEquals,
14 assertThrows,
15 describe,
16 it,
17 } from "./dev-deps.js";
18 import {
19 asciiLowercase,
20 asciiUppercase,
21 codepoints,
22 codeUnits,
23 getCharacter,
24 join,
25 Matcher,
26 scalarValues,
27 scalarValueString,
28 splitOnASCIIWhitespace,
29 splitOnCommas,
30 stripAndCollapseASCIIWhitespace,
31 stripLeadingAndTrailingASCIIWhitespace,
32 toString,
33 } from "./string.js";
34
35 describe("Matcher", () => {
36 it("[[Construct]] accepts a string first argument", () => {
37 assert(new Matcher(""));
38 });
39
40 it("[[Construct]] accepts a unicode regular expression first argument", () => {
41 assert(new Matcher(/(?:)/u));
42 });
43
44 it("[[Construct]] throws with a nonยทunicode regular expression first argument", () => {
45 assertThrows(() => new Matcher(/(?:)/));
46 });
47
48 it("[[Construct]] creates a callable object", () => {
49 assertStrictEquals(typeof new Matcher(""), "function");
50 });
51
52 it("[[Construct]] creates a new Matcher", () => {
53 assertStrictEquals(
54 Object.getPrototypeOf(new Matcher("")),
55 Matcher.prototype,
56 );
57 });
58
59 it("[[Construct]] creates an object which inherits from RegExp", () => {
60 assert(new Matcher("") instanceof RegExp);
61 });
62
63 describe("::dotAll", () => {
64 it("[[Get]] returns true when the dotAll flag is present", () => {
65 assertStrictEquals(new Matcher(/(?:)/su).dotAll, true);
66 });
67
68 it("[[Get]] returns false when the dotAll flag is not present", () => {
69 assertStrictEquals(new Matcher(/(?:)/u).dotAll, false);
70 });
71 });
72
73 describe("::exec", () => {
74 it("[[Call]] returns the match object given a complete match", () => {
75 assertEquals(
76 [...new Matcher(/.(?<wow>(?:.(?=.))*)(.)?/u).exec("success")],
77 ["success", "ucces", "s"],
78 );
79 });
80
81 it("[[Call]] returns null given a partial match", () => {
82 assertEquals(new Matcher("").exec("failure"), null);
83 });
84 });
85
86 describe("::global", () => {
87 it("[[Get]] returns true when the global flag is present", () => {
88 assertStrictEquals(new Matcher(/(?:)/gu).global, true);
89 });
90
91 it("[[Get]] returns false when the global flag is not present", () => {
92 assertStrictEquals(new Matcher(/(?:)/u).global, false);
93 });
94 });
95
96 describe("::hasIndices", () => {
97 it("[[Get]] returns true when the hasIndices flag is present", () => {
98 assertStrictEquals(new Matcher(/(?:)/du).hasIndices, true);
99 });
100
101 it("[[Get]] returns false when the hasIndices flag is not present", () => {
102 assertStrictEquals(new Matcher(/(?:)/u).hasIndices, false);
103 });
104 });
105
106 describe("::ignoreCase", () => {
107 it("[[Get]] returns true when the ignoreCase flag is present", () => {
108 assertStrictEquals(new Matcher(/(?:)/iu).ignoreCase, true);
109 });
110
111 it("[[Get]] returns false when the ignoreCase flag is not present", () => {
112 assertStrictEquals(new Matcher(/(?:)/u).ignoreCase, false);
113 });
114 });
115
116 describe("::multiline", () => {
117 it("[[Get]] returns true when the multiline flag is present", () => {
118 assertStrictEquals(new Matcher(/(?:)/mu).multiline, true);
119 });
120
121 it("[[Get]] returns false when the multiline flag is not present", () => {
122 assertStrictEquals(new Matcher(/(?:)/u).multiline, false);
123 });
124 });
125
126 describe("::source", () => {
127 it("[[Get]] returns the RegExp source", () => {
128 assertStrictEquals(new Matcher("").source, "(?:)");
129 assertStrictEquals(new Matcher(/.*/su).source, ".*");
130 });
131 });
132
133 describe("::sticky", () => {
134 it("[[Get]] returns true when the sticky flag is present", () => {
135 assertStrictEquals(new Matcher(/(?:)/uy).sticky, true);
136 });
137
138 it("[[Get]] returns false when the sticky flag is not present", () => {
139 assertStrictEquals(new Matcher(/(?:)/u).sticky, false);
140 });
141 });
142
143 describe("::unicode", () => {
144 it("[[Get]] returns true when the unicode flag is present", () => {
145 assertStrictEquals(new Matcher(/(?:)/u).unicode, true);
146 });
147 });
148
149 describe("~", () => {
150 it("[[Call]] returns true for a complete match", () => {
151 assertStrictEquals(new Matcher("")(""), true);
152 assertStrictEquals(new Matcher(/.*/su)("success\nyay"), true);
153 });
154
155 it("[[Call]] returns false for a partial match", () => {
156 assertStrictEquals(new Matcher("")("failure"), false);
157 assertStrictEquals(new Matcher(/.*/u)("failure\nno"), false);
158 });
159 });
160
161 describe("~lastIndex", () => {
162 it("[[Get]] returns zero", () => {
163 assertStrictEquals(new Matcher("").lastIndex, 0);
164 });
165
166 it("[[Set]] fails", () => {
167 assertThrows(() => (new Matcher("").lastIndex = 1));
168 });
169 });
170
171 describe("~length", () => {
172 it("[[Get]] returns one", () => {
173 assertStrictEquals(new Matcher("").length, 1);
174 });
175 });
176
177 describe("~name", () => {
178 it("[[Get]] wraps the stringified regular expression if no name was provided", () => {
179 assertStrictEquals(new Matcher("").name, "Matcher(/(?:)/u)");
180 assertStrictEquals(
181 new Matcher(/.*/gsu).name,
182 "Matcher(/.*/gsu)",
183 );
184 });
185
186 it("[[Get]] uses the provided name if one was provided", () => {
187 assertStrictEquals(new Matcher("", "success").name, "success");
188 });
189 });
190 });
191
192 describe("asciiLowercase", () => {
193 it("[[Call]] lowercases (just) AยทSยทCยทIยทI letters", () => {
194 assertStrictEquals(asciiLowercase("aBลฟร†ss Ftษษ‚รŸ"), "abลฟร†ss ftษษ‚รŸ");
195 });
196 });
197
198 describe("asciiUppercase", () => {
199 it("[[Call]] uppercases (just) AยทSยทCยทIยทI letters", () => {
200 assertStrictEquals(asciiUppercase("aBลฟร†ss Ftษษ‚รŸ"), "ABลฟร†SS FTษษ‚รŸ");
201 });
202 });
203
204 describe("codeUnits", () => {
205 it("[[Call]] returns an iterable", () => {
206 assertStrictEquals(
207 typeof codeUnits("")[Symbol.iterator],
208 "function",
209 );
210 });
211
212 it("[[Call]] returns an iterator", () => {
213 assertStrictEquals(typeof codeUnits("").next, "function");
214 });
215
216 it("[[Call]] returns a string code value iterator", () => {
217 assertStrictEquals(
218 codeUnits("")[Symbol.toStringTag],
219 "String Code Value Iterator",
220 );
221 });
222
223 it("[[Call]] iterates over the code units", () => {
224 assertEquals([
225 ...codeUnits("Ii๐ŸŽ™\uDFFF\uDD96\uD83C\uD800๐Ÿ†—โ˜บ"),
226 ], [
227 0x49,
228 0x69,
229 0xD83C,
230 0xDF99,
231 0xDFFF,
232 0xDD96,
233 0xD83C,
234 0xD800,
235 0xD83C,
236 0xDD97,
237 0x263A,
238 ]);
239 });
240 });
241
242 describe("codepoints", () => {
243 it("[[Call]] returns an iterable", () => {
244 assertStrictEquals(
245 typeof codepoints("")[Symbol.iterator],
246 "function",
247 );
248 });
249
250 it("[[Call]] returns an iterator", () => {
251 assertStrictEquals(typeof codepoints("").next, "function");
252 });
253
254 it("[[Call]] returns a string code value iterator", () => {
255 assertStrictEquals(
256 codepoints("")[Symbol.toStringTag],
257 "String Code Value Iterator",
258 );
259 });
260
261 it("[[Call]] iterates over the codepoints", () => {
262 assertEquals([
263 ...codepoints("Ii๐ŸŽ™\uDFFF\uDD96\uD83C\uD800๐Ÿ†—โ˜บ"),
264 ], [
265 0x49,
266 0x69,
267 0x1F399,
268 0xDFFF,
269 0xDD96,
270 0xD83C,
271 0xD800,
272 0x1F197,
273 0x263A,
274 ]);
275 });
276 });
277
278 describe("getCharacter", () => {
279 it("[[Call]] returns the character at the provided position", () => {
280 assertStrictEquals(getCharacter("Ii๐ŸŽ™๐Ÿ†—โ˜บ", 4), "๐Ÿ†—");
281 });
282
283 it("[[Call]] returns a low surrogate if the provided position splits a character", () => {
284 assertStrictEquals(getCharacter("Ii๐ŸŽ™๐Ÿ†—โ˜บ", 5), "\uDD97");
285 });
286
287 it("[[Call]] returns undefined for an outโ€ofโ€bounds index", () => {
288 assertStrictEquals(getCharacter("Ii๐ŸŽ™๐Ÿ†—โ˜บ", -1), void {});
289 assertStrictEquals(getCharacter("Ii๐ŸŽ™๐Ÿ†—โ˜บ", 7), void {});
290 });
291 });
292
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");
296 });
297
298 it('[[Call]] uses "," if no separator is provided', () => {
299 assertStrictEquals(join([1, 2, 3, 4].values()), "1,2,3,4");
300 });
301
302 it("[[Call]] uses the empty sting for nullish values", () => {
303 assertStrictEquals(
304 join([null, , null, undefined].values(), "โ˜‚"),
305 "โ˜‚โ˜‚โ˜‚",
306 );
307 });
308 });
309
310 describe("scalarValueString", () => {
311 it("[[Call]] replaces invalid values", () => {
312 assertStrictEquals(
313 scalarValueString("Ii๐ŸŽ™\uDFFF\uDD96\uD83C\uD800๐Ÿ†—โ˜บ"),
314 "Ii๐ŸŽ™\uFFFD\uFFFD\uFFFD\uFFFD๐Ÿ†—โ˜บ",
315 );
316 });
317 });
318
319 describe("scalarValues", () => {
320 it("[[Call]] returns an iterable", () => {
321 assertStrictEquals(
322 typeof scalarValues("")[Symbol.iterator],
323 "function",
324 );
325 });
326
327 it("[[Call]] returns an iterator", () => {
328 assertStrictEquals(typeof scalarValues("").next, "function");
329 });
330
331 it("[[Call]] returns a string code value iterator", () => {
332 assertStrictEquals(
333 scalarValues("")[Symbol.toStringTag],
334 "String Code Value Iterator",
335 );
336 });
337
338 it("[[Call]] iterates over the scalar values", () => {
339 assertEquals([
340 ...scalarValues("Ii๐ŸŽ™\uDFFF\uDD96\uD83C\uD800๐Ÿ†—โ˜บ"),
341 ], [
342 0x49,
343 0x69,
344 0x1F399,
345 0xFFFD,
346 0xFFFD,
347 0xFFFD,
348 0xFFFD,
349 0x1F197,
350 0x263A,
351 ]);
352 });
353 });
354
355 describe("splitOnASCIIWhitespace", () => {
356 it("[[Call]] splits on sequences of spaces", () => {
357 assertEquals(
358 splitOnASCIIWhitespace("๐Ÿ…ฐ๏ธ ๐Ÿ…ฑ๏ธ ๐Ÿ†Ž ๐Ÿ…พ๏ธ"),
359 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
360 );
361 });
362
363 it("[[Call]] splits on sequences of tabs", () => {
364 assertEquals(
365 splitOnASCIIWhitespace("๐Ÿ…ฐ๏ธ\t\t\t๐Ÿ…ฑ๏ธ\t๐Ÿ†Ž\t\t๐Ÿ…พ๏ธ"),
366 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
367 );
368 });
369
370 it("[[Call]] splits on sequences of carriage returns", () => {
371 assertEquals(
372 splitOnASCIIWhitespace("๐Ÿ…ฐ๏ธ\r\r\r๐Ÿ…ฑ๏ธ\r๐Ÿ†Ž\r\r๐Ÿ…พ๏ธ"),
373 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
374 );
375 });
376
377 it("[[Call]] splits on sequences of newlines", () => {
378 assertEquals(
379 splitOnASCIIWhitespace("๐Ÿ…ฐ๏ธ\r\r\r๐Ÿ…ฑ๏ธ\r๐Ÿ†Ž\r\r๐Ÿ…พ๏ธ"),
380 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
381 );
382 });
383
384 it("[[Call]] splits on sequences of form feeds", () => {
385 assertEquals(
386 splitOnASCIIWhitespace("๐Ÿ…ฐ๏ธ\f\f\f๐Ÿ…ฑ๏ธ\f๐Ÿ†Ž\f\f๐Ÿ…พ๏ธ"),
387 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
388 );
389 });
390
391 it("[[Call]] splits on mixed whitespace", () => {
392 assertEquals(
393 splitOnASCIIWhitespace("๐Ÿ…ฐ๏ธ\f \t\n๐Ÿ…ฑ๏ธ\r\n\r๐Ÿ†Ž\n\f๐Ÿ…พ๏ธ"),
394 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
395 );
396 });
397
398 it("[[Call]] returns an array of just the empty string for the empty string", () => {
399 assertEquals(splitOnASCIIWhitespace(""), [""]);
400 });
401
402 it("[[Call]] returns a single token if there are no spaces", () => {
403 assertEquals(splitOnASCIIWhitespace("abcd"), ["abcd"]);
404 });
405
406 it("[[Call]] does not split on other kinds of whitespace", () => {
407 assertEquals(
408 splitOnASCIIWhitespace("a\u202F\u205F\xa0\v\0\bb"),
409 ["a\u202F\u205F\xa0\v\0\bb"],
410 );
411 });
412
413 it("[[Call]] trims leading and trailing whitespace", () => {
414 assertEquals(
415 splitOnASCIIWhitespace(
416 "\f\r\n\r\n \n\t๐Ÿ…ฐ๏ธ\f \t\n๐Ÿ…ฑ๏ธ\r๐Ÿ†Ž\n\f๐Ÿ…พ๏ธ\n\f",
417 ),
418 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
419 );
420 });
421 });
422
423 describe("splitOnCommas", () => {
424 it("[[Call]] splits on commas", () => {
425 assertEquals(
426 splitOnCommas("๐Ÿ…ฐ๏ธ,๐Ÿ…ฑ๏ธ,๐Ÿ†Ž,๐Ÿ…พ๏ธ"),
427 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
428 );
429 });
430
431 it("[[Call]] returns an array of just the empty string for the empty string", () => {
432 assertEquals(splitOnCommas(""), [""]);
433 });
434
435 it("[[Call]] returns a single token if there are no commas", () => {
436 assertEquals(splitOnCommas("abcd"), ["abcd"]);
437 });
438
439 it("[[Call]] splits into empty strings if there are only commas", () => {
440 assertEquals(splitOnCommas(",,,"), ["", "", "", ""]);
441 });
442
443 it("[[Call]] trims leading and trailing whitespace", () => {
444 assertEquals(
445 splitOnCommas("\f\r\n\r\n \n\t๐Ÿ…ฐ๏ธ,๐Ÿ…ฑ๏ธ,๐Ÿ†Ž,๐Ÿ…พ๏ธ\n\f"),
446 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
447 );
448 assertEquals(
449 splitOnCommas("\f\r\n\r\n \n\t,,,\n\f"),
450 ["", "", "", ""],
451 );
452 });
453
454 it("[[Call]] removes whitespace from the split tokens", () => {
455 assertEquals(
456 splitOnCommas(
457 "\f\r\n\r\n \n\t๐Ÿ…ฐ๏ธ\f , \t\n๐Ÿ…ฑ๏ธ,\r\n\r๐Ÿ†Ž\n\f,๐Ÿ…พ๏ธ\n\f",
458 ),
459 ["๐Ÿ…ฐ๏ธ", "๐Ÿ…ฑ๏ธ", "๐Ÿ†Ž", "๐Ÿ…พ๏ธ"],
460 );
461 assertEquals(
462 splitOnCommas("\f\r\n\r\n \n\t\f , \t\n,\r\n\r\n\f,\n\f"),
463 ["", "", "", ""],
464 );
465 });
466 });
467
468 describe("stripAndCollapseASCIIWhitespace", () => {
469 it("[[Call]] collapses mixed inner whitespace", () => {
470 assertEquals(
471 stripAndCollapseASCIIWhitespace("๐Ÿ…ฐ๏ธ\f \t\n๐Ÿ…ฑ๏ธ\r\n\r๐Ÿ†Ž\n\f๐Ÿ…พ๏ธ"),
472 "๐Ÿ…ฐ๏ธ ๐Ÿ…ฑ๏ธ ๐Ÿ†Ž ๐Ÿ…พ๏ธ",
473 );
474 });
475
476 it("[[Call]] trims leading and trailing whitespace", () => {
477 assertStrictEquals(
478 stripAndCollapseASCIIWhitespace(
479 "\f\r\n\r\n \n\t\f ๐Ÿ…ฐ๏ธ\f \t\n๐Ÿ…ฑ๏ธ\r\n\r๐Ÿ†Ž\n\f๐Ÿ…พ๏ธ\n\f",
480 ),
481 "๐Ÿ…ฐ๏ธ ๐Ÿ…ฑ๏ธ ๐Ÿ†Ž ๐Ÿ…พ๏ธ",
482 );
483 });
484
485 it("[[Call]] returns the empty string for strings of whitespace", () => {
486 assertStrictEquals(
487 stripAndCollapseASCIIWhitespace("\f\r\n\r\n \n\t\f \n\f"),
488 "",
489 );
490 });
491
492 it("[[Call]] does not collapse other kinds of whitespace", () => {
493 assertEquals(
494 stripAndCollapseASCIIWhitespace("a\u202F\u205F\xa0\v\0\bb"),
495 "a\u202F\u205F\xa0\v\0\bb",
496 );
497 });
498 });
499
500 describe("stripLeadingAndTrailingASCIIWhitespace", () => {
501 it("[[Call]] trims leading and trailing whitespace", () => {
502 assertStrictEquals(
503 stripLeadingAndTrailingASCIIWhitespace(
504 "\f\r\n\r\n \n\t\f ๐Ÿ…ฐ๏ธ๐Ÿ…ฑ๏ธ๐Ÿ†Ž๐Ÿ…พ๏ธ\n\f",
505 ),
506 "๐Ÿ…ฐ๏ธ๐Ÿ…ฑ๏ธ๐Ÿ†Ž๐Ÿ…พ๏ธ",
507 );
508 });
509
510 it("[[Call]] returns the empty string for strings of whitespace", () => {
511 assertStrictEquals(
512 stripLeadingAndTrailingASCIIWhitespace("\f\r\n\r\n \n\t\f \n\f"),
513 "",
514 );
515 });
516
517 it("[[Call]] does not trim other kinds of whitespace", () => {
518 assertEquals(
519 stripLeadingAndTrailingASCIIWhitespace(
520 "\v\u202F\u205Fx\0\b\xa0",
521 ),
522 "\v\u202F\u205Fx\0\b\xa0",
523 );
524 });
525
526 it("[[Call]] does not adjust inner whitespace", () => {
527 assertEquals(
528 stripLeadingAndTrailingASCIIWhitespace("a b"),
529 "a b",
530 );
531 });
532 });
533
534 describe("toString", () => {
535 it("[[Call]] converts to a string", () => {
536 assertStrictEquals(
537 toString({
538 toString() {
539 return "success";
540 },
541 }),
542 "success",
543 );
544 });
545
546 it("[[Call]] throws when provided a symbol", () => {
547 assertThrows(() => toString(Symbol()));
548 });
549 });
This page took 0.093485 seconds and 3 git commands to generate.