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