| 1 | import { Option } from "effect" |
| 2 | import { pipe } from "effect/Function" |
| 3 | import * as S from "effect/String" |
| 4 | import { assert, describe, it } from "vitest" |
| 5 | import { assertFalse, assertNone, assertSome, assertTrue, deepStrictEqual, strictEqual } from "./utils/assert.ts" |
| 6 | |
| 7 | describe("String", () => { |
| 8 | describe("String (constructor)", () => { |
| 9 | it("converts values to strings", () => { |
| 10 | strictEqual(S.String(123), "123") |
| 11 | strictEqual(S.String(true), "true") |
| 12 | strictEqual(S.String(null), "null") |
| 13 | }) |
| 14 | }) |
| 15 | |
| 16 | describe("isString", () => { |
| 17 | it("returns true for strings", () => { |
| 18 | assertTrue(S.isString("hello")) |
| 19 | assertTrue(S.isString("")) |
| 20 | }) |
| 21 | |
| 22 | it("returns false for non-strings", () => { |
| 23 | assertFalse(S.isString(1)) |
| 24 | assertFalse(S.isString(null)) |
| 25 | assertFalse(S.isString(undefined)) |
| 26 | assertFalse(S.isString(true)) |
| 27 | assertFalse(S.isString({})) |
| 28 | }) |
| 29 | }) |
| 30 | |
| 31 | describe("Order", () => { |
| 32 | it("compares strings lexicographically", () => { |
| 33 | strictEqual(S.Order("apple", "banana"), -1) |
| 34 | strictEqual(S.Order("banana", "apple"), 1) |
| 35 | strictEqual(S.Order("apple", "apple"), 0) |
| 36 | }) |
| 37 | |
| 38 | it("compares empty strings", () => { |
| 39 | strictEqual(S.Order("", ""), 0) |
| 40 | strictEqual(S.Order("", "a"), -1) |
| 41 | strictEqual(S.Order("a", ""), 1) |
| 42 | }) |
| 43 | }) |
| 44 | |
| 45 | describe("Equivalence", () => { |
| 46 | it("returns true for equal strings", () => { |
| 47 | assertTrue(S.Equivalence("a", "a")) |
| 48 | }) |
| 49 | |
| 50 | it("returns false for different strings", () => { |
| 51 | assertFalse(S.Equivalence("a", "b")) |
| 52 | }) |
| 53 | }) |
| 54 | |
| 55 | describe("empty", () => { |
| 56 | it("is the empty string", () => { |
| 57 | strictEqual(S.empty, "") |
| 58 | }) |
| 59 | }) |
| 60 | |
| 61 | describe("concat", () => { |
| 62 | it("data-first", () => { |
| 63 | strictEqual(S.concat("hello", " world"), "hello world") |
| 64 | }) |
| 65 | |
| 66 | it("data-last (pipeable)", () => { |
| 67 | strictEqual(pipe("hello", S.concat(" world")), "hello world") |
| 68 | }) |
| 69 | |
| 70 | it("concatenates empty strings", () => { |
| 71 | strictEqual(S.concat("", ""), "") |
| 72 | strictEqual(S.concat("a", ""), "a") |
| 73 | strictEqual(S.concat("", "b"), "b") |
| 74 | }) |
| 75 | }) |
| 76 | |
| 77 | describe("toUpperCase", () => { |
| 78 | it("converts to uppercase", () => { |
| 79 | strictEqual(S.toUpperCase("hello"), "HELLO") |
| 80 | }) |
| 81 | |
| 82 | it("handles empty string", () => { |
| 83 | strictEqual(S.toUpperCase(""), "") |
| 84 | }) |
| 85 | |
| 86 | it("pipeable", () => { |
| 87 | strictEqual(pipe("abc", S.toUpperCase), "ABC") |
| 88 | }) |
| 89 | }) |
| 90 | |
| 91 | describe("toLowerCase", () => { |
| 92 | it("converts to lowercase", () => { |
| 93 | strictEqual(S.toLowerCase("HELLO"), "hello") |
| 94 | }) |
| 95 | |
| 96 | it("handles empty string", () => { |
| 97 | strictEqual(S.toLowerCase(""), "") |
| 98 | }) |
| 99 | }) |
| 100 | |
| 101 | describe("capitalize", () => { |
| 102 | it("capitalizes first character", () => { |
| 103 | strictEqual(S.capitalize("hello"), "Hello") |
| 104 | }) |
| 105 | |
| 106 | it("handles single character", () => { |
| 107 | strictEqual(S.capitalize("a"), "A") |
| 108 | }) |
| 109 | |
| 110 | it("handles empty string", () => { |
| 111 | strictEqual(S.capitalize(""), "") |
| 112 | }) |
| 113 | |
| 114 | it("does not change already capitalized", () => { |
| 115 | strictEqual(S.capitalize("Hello"), "Hello") |
| 116 | }) |
| 117 | }) |
| 118 | |
| 119 | describe("uncapitalize", () => { |
| 120 | it("uncapitalizes first character", () => { |
| 121 | strictEqual(S.uncapitalize("Hello"), "hello") |
| 122 | }) |
| 123 | |
| 124 | it("handles single character", () => { |
| 125 | strictEqual(S.uncapitalize("A"), "a") |
| 126 | }) |
| 127 | |
| 128 | it("handles empty string", () => { |
| 129 | strictEqual(S.uncapitalize(""), "") |
| 130 | }) |
| 131 | |
| 132 | it("preserves rest of string", () => { |
| 133 | strictEqual(S.uncapitalize("ABC"), "aBC") |
| 134 | }) |
| 135 | }) |
| 136 | |
| 137 | describe("replace", () => { |
| 138 | it("replaces first occurrence", () => { |
| 139 | strictEqual(pipe("abab", S.replace("b", "c")), "acab") |
| 140 | }) |
| 141 | |
| 142 | it("replaces with regex", () => { |
| 143 | strictEqual(pipe("abc", S.replace(/b/, "d")), "adc") |
| 144 | }) |
| 145 | |
| 146 | it("returns unchanged if no match", () => { |
| 147 | strictEqual(pipe("abc", S.replace("z", "x")), "abc") |
| 148 | }) |
| 149 | }) |
| 150 | |
| 151 | describe("trim", () => { |
| 152 | it("trims whitespace from both ends", () => { |
| 153 | strictEqual(S.trim(" hello "), "hello") |
| 154 | }) |
| 155 | |
| 156 | it("trims tabs and newlines", () => { |
| 157 | strictEqual(S.trim("\t\nhello\t\n"), "hello") |
| 158 | }) |
| 159 | |
| 160 | it("handles empty string", () => { |
| 161 | strictEqual(S.trim(""), "") |
| 162 | }) |
| 163 | }) |
| 164 | |
| 165 | describe("trimStart", () => { |
| 166 | it("trims leading whitespace", () => { |
| 167 | strictEqual(S.trimStart(" hello "), "hello ") |
| 168 | }) |
| 169 | |
| 170 | it("handles empty string", () => { |
| 171 | strictEqual(S.trimStart(""), "") |
| 172 | }) |
| 173 | }) |
| 174 | |
| 175 | describe("trimEnd", () => { |
| 176 | it("trims trailing whitespace", () => { |
| 177 | strictEqual(S.trimEnd(" hello "), " hello") |
| 178 | }) |
| 179 | |
| 180 | it("handles empty string", () => { |
| 181 | strictEqual(S.trimEnd(""), "") |
| 182 | }) |
| 183 | }) |
| 184 | |
| 185 | describe("slice", () => { |
| 186 | it("extracts a section", () => { |
| 187 | strictEqual(pipe("abcd", S.slice(1, 3)), "bc") |
| 188 | }) |
| 189 | |
| 190 | it("handles negative indices", () => { |
| 191 | strictEqual(pipe("hello", S.slice(-3)), "llo") |
| 192 | }) |
| 193 | |
| 194 | it("no arguments returns full string", () => { |
| 195 | strictEqual(pipe("hello", S.slice()), "hello") |
| 196 | }) |
| 197 | }) |
| 198 | |
| 199 | describe("isEmpty", () => { |
| 200 | it("returns true for empty string", () => { |
| 201 | assertTrue(S.isEmpty("")) |
| 202 | }) |
| 203 | |
| 204 | it("returns false for non-empty string", () => { |
| 205 | assertFalse(S.isEmpty("a")) |
| 206 | assertFalse(S.isEmpty(" ")) |
| 207 | }) |
| 208 | }) |
| 209 | |
| 210 | describe("isNonEmpty", () => { |
| 211 | it("returns true for non-empty string", () => { |
| 212 | assertTrue(S.isNonEmpty("a")) |
| 213 | }) |
| 214 | |
| 215 | it("returns false for empty string", () => { |
| 216 | assertFalse(S.isNonEmpty("")) |
| 217 | }) |
| 218 | }) |
| 219 | |
| 220 | describe("length", () => { |
| 221 | it("returns string length", () => { |
| 222 | strictEqual(S.length("abc"), 3) |
| 223 | }) |
| 224 | |
| 225 | it("returns 0 for empty string", () => { |
| 226 | strictEqual(S.length(""), 0) |
| 227 | }) |
| 228 | }) |
| 229 | |
| 230 | describe("split", () => { |
| 231 | it("splits by string separator (data-last)", () => { |
| 232 | deepStrictEqual(pipe("a,b,c", S.split(",")), ["a", "b", "c"]) |
| 233 | }) |
| 234 | |
| 235 | it("splits by string separator (data-first)", () => { |
| 236 | deepStrictEqual(S.split("hello,world", ","), ["hello", "world"]) |
| 237 | }) |
| 238 | |
| 239 | it("splits by empty string", () => { |
| 240 | deepStrictEqual(pipe("abc", S.split("")), ["a", "b", "c"]) |
| 241 | }) |
| 242 | |
| 243 | it("splits empty string by empty string", () => { |
| 244 | deepStrictEqual(pipe("", S.split("")), [""]) |
| 245 | }) |
| 246 | |
| 247 | it("splits by regex", () => { |
| 248 | deepStrictEqual(pipe("a1b2c", S.split(/\d/)), ["a", "b", "c"]) |
| 249 | }) |
| 250 | }) |
| 251 | |
| 252 | describe("includes", () => { |
| 253 | it("returns true when substring found", () => { |
| 254 | assertTrue(pipe("hello world", S.includes("world"))) |
| 255 | }) |
| 256 | |
| 257 | it("returns false when substring not found", () => { |
| 258 | assertFalse(pipe("hello world", S.includes("foo"))) |
| 259 | }) |
| 260 | |
| 261 | it("starts searching at the given position", () => { |
| 262 | assertFalse(pipe("hello", S.includes("hel", 1))) |
| 263 | assertTrue(pipe("hello", S.includes("ell", 1))) |
| 264 | }) |
| 265 | }) |
| 266 | |
| 267 | describe("startsWith", () => { |
| 268 | it("returns true when string starts with search", () => { |
| 269 | assertTrue(pipe("hello world", S.startsWith("hello"))) |
| 270 | }) |
| 271 | |
| 272 | it("returns false when string does not start with search", () => { |
| 273 | assertFalse(pipe("hello world", S.startsWith("world"))) |
| 274 | }) |
| 275 | |
| 276 | it("checks the prefix at the given position", () => { |
| 277 | assertTrue(pipe("hello world", S.startsWith("world", 6))) |
| 278 | }) |
| 279 | }) |
| 280 | |
| 281 | describe("endsWith", () => { |
| 282 | it("returns true when string ends with search", () => { |
| 283 | assertTrue(pipe("hello world", S.endsWith("world"))) |
| 284 | }) |
| 285 | |
| 286 | it("returns false when string does not end with search", () => { |
| 287 | assertFalse(pipe("hello world", S.endsWith("hello"))) |
| 288 | }) |
| 289 | |
| 290 | it("checks the suffix at the given end position", () => { |
| 291 | assertTrue(pipe("hello world", S.endsWith("hello", 5))) |
| 292 | }) |
| 293 | }) |
| 294 | |
| 295 | describe("charCodeAt", () => { |
| 296 | it("returns char code at index (data-first)", () => { |
| 297 | assertSome(S.charCodeAt("abc", 1), 98) |
| 298 | }) |
| 299 | |
| 300 | it("returns char code at index (data-last)", () => { |
| 301 | assertSome(pipe("abc", S.charCodeAt(0)), 97) |
| 302 | }) |
| 303 | |
| 304 | it("returns none for out of bounds", () => { |
| 305 | assertNone(S.charCodeAt("abc", 10)) |
| 306 | }) |
| 307 | }) |
| 308 | |
| 309 | describe("substring", () => { |
| 310 | it("extracts characters between indices", () => { |
| 311 | strictEqual(pipe("abcd", S.substring(1, 3)), "bc") |
| 312 | }) |
| 313 | |
| 314 | it("extracts from start to end", () => { |
| 315 | strictEqual(pipe("abcd", S.substring(1)), "bcd") |
| 316 | }) |
| 317 | }) |
| 318 | |
| 319 | describe("at", () => { |
| 320 | it("returns character at index", () => { |
| 321 | assertSome(pipe("abc", S.at(1)), "b") |
| 322 | }) |
| 323 | |
| 324 | it("returns none for out of bounds", () => { |
| 325 | assertNone(pipe("abc", S.at(10))) |
| 326 | }) |
| 327 | }) |
| 328 | |
| 329 | describe("charAt", () => { |
| 330 | it("returns character at index (data-first)", () => { |
| 331 | assertSome(S.charAt("abc", 1), "b") |
| 332 | }) |
| 333 | |
| 334 | it("returns character at index (data-last)", () => { |
| 335 | assertSome(pipe("abc", S.charAt(1)), "b") |
| 336 | }) |
| 337 | |
| 338 | it("returns none for out of bounds", () => { |
| 339 | assertNone(S.charAt("abc", 10)) |
| 340 | }) |
| 341 | }) |
| 342 | |
| 343 | describe("codePointAt", () => { |
| 344 | it("returns code point at index", () => { |
| 345 | assertSome(pipe("abc", S.codePointAt(1)), 98) |
| 346 | }) |
| 347 | |
| 348 | it("returns none for out of bounds", () => { |
| 349 | assertNone(pipe("abc", S.codePointAt(10))) |
| 350 | }) |
| 351 | |
| 352 | it("handles surrogate pairs", () => { |
| 353 | assertSome(pipe("𝟘", S.codePointAt(0)), 120792) |
| 354 | }) |
| 355 | }) |
| 356 | |
| 357 | describe("indexOf", () => { |
| 358 | it("returns index of first occurrence", () => { |
| 359 | assertSome(pipe("abbbc", S.indexOf("b")), 1) |
| 360 | }) |
| 361 | |
| 362 | it("returns none when not found", () => { |
| 363 | assertNone(pipe("abbbc", S.indexOf("z"))) |
| 364 | }) |
| 365 | }) |
| 366 | |
| 367 | describe("lastIndexOf", () => { |
| 368 | it("returns index of last occurrence", () => { |
| 369 | assertSome(pipe("abbbc", S.lastIndexOf("b")), 3) |
| 370 | }) |
| 371 | |
| 372 | it("returns none when not found", () => { |
| 373 | assertNone(pipe("abbbc", S.lastIndexOf("z"))) |
| 374 | }) |
| 375 | }) |
| 376 | |
| 377 | describe("localeCompare", () => { |
| 378 | it("returns ordering", () => { |
| 379 | strictEqual(pipe("a", S.localeCompare("b")), -1) |
| 380 | strictEqual(pipe("b", S.localeCompare("a")), 1) |
| 381 | strictEqual(pipe("a", S.localeCompare("a")), 0) |
| 382 | }) |
| 383 | }) |
| 384 | |
| 385 | describe("match", () => { |
| 386 | it("returns matched text and native match metadata", () => { |
| 387 | const result = pipe("hello", S.match(/l+/)) |
| 388 | assert(Option.isSome(result)) |
| 389 | strictEqual(result.value[0], "ll") |
| 390 | }) |
| 391 | |
| 392 | it("returns none when the pattern does not match", () => { |
| 393 | assertNone(pipe("hello", S.match(/x/))) |
| 394 | }) |
| 395 | }) |
| 396 | |
| 397 | describe("matchAll", () => { |
| 398 | it("returns all matches", () => { |
| 399 | const result = Array.from(pipe("hello world", S.matchAll(/l/g))) |
| 400 | strictEqual(result.length, 3) |
| 401 | }) |
| 402 | }) |
| 403 | |
| 404 | describe("normalize", () => { |
| 405 | it("normalizes with different forms", () => { |
| 406 | const str = "\u1E9B\u0323" |
| 407 | strictEqual(pipe(str, S.normalize("NFD")), "\u017F\u0323\u0307") |
| 408 | strictEqual(pipe(str, S.normalize("NFKC")), "\u1E69") |
| 409 | strictEqual(pipe(str, S.normalize("NFKD")), "\u0073\u0323\u0307") |
| 410 | }) |
| 411 | }) |
| 412 | |
| 413 | describe("padEnd", () => { |
| 414 | it("pads from end with spaces", () => { |
| 415 | strictEqual(pipe("a", S.padEnd(5)), "a ") |
| 416 | }) |
| 417 | |
| 418 | it("pads from end with fill string", () => { |
| 419 | strictEqual(pipe("a", S.padEnd(5, "_")), "a____") |
| 420 | }) |
| 421 | |
| 422 | it("does not truncate if already longer", () => { |
| 423 | strictEqual(pipe("hello", S.padEnd(3)), "hello") |
| 424 | }) |
| 425 | }) |
| 426 | |
| 427 | describe("padStart", () => { |
| 428 | it("pads from start with spaces", () => { |
| 429 | strictEqual(pipe("a", S.padStart(5)), " a") |
| 430 | }) |
| 431 | |
| 432 | it("pads from start with fill string", () => { |
| 433 | strictEqual(pipe("a", S.padStart(5, "_")), "____a") |
| 434 | }) |
| 435 | |
| 436 | it("does not truncate if already longer", () => { |
| 437 | strictEqual(pipe("hello", S.padStart(3)), "hello") |
| 438 | }) |
| 439 | }) |
| 440 | |
| 441 | describe("repeat", () => { |
| 442 | it("repeats the string", () => { |
| 443 | strictEqual(pipe("ab", S.repeat(3)), "ababab") |
| 444 | strictEqual(pipe("ab", S.repeat(1.5)), "ab") |
| 445 | }) |
| 446 | |
| 447 | it("returns an empty string for non-positive or NaN counts", () => { |
| 448 | strictEqual(pipe("ab", S.repeat(0)), "") |
| 449 | strictEqual(pipe("ab", S.repeat(-1)), "") |
| 450 | strictEqual(pipe("ab", S.repeat(Number.NaN)), "") |
| 451 | }) |
| 452 | }) |
| 453 | |
| 454 | describe("replaceAll", () => { |
| 455 | it("replaces all occurrences with string", () => { |
| 456 | strictEqual(pipe("ababb", S.replaceAll("b", "c")), "acacc") |
| 457 | }) |
| 458 | |
| 459 | it("replaces all occurrences with regex", () => { |
| 460 | strictEqual(pipe("ababb", S.replaceAll(/b/g, "c")), "acacc") |
| 461 | }) |
| 462 | }) |
| 463 | |
| 464 | describe("search", () => { |
| 465 | it("returns index on match (data-first)", () => { |
| 466 | assertSome(S.search("ababb", "b"), 1) |
| 467 | }) |
| 468 | |
| 469 | it("returns index on match (data-last)", () => { |
| 470 | assertSome(pipe("ababb", S.search("b")), 1) |
| 471 | }) |
| 472 | |
| 473 | it("returns none when not found", () => { |
| 474 | assertNone(S.search("ababb", "d")) |
| 475 | }) |
| 476 | |
| 477 | it("works with regex", () => { |
| 478 | assertSome(S.search("ababb", /bb/), 3) |
| 479 | }) |
| 480 | }) |
| 481 | |
| 482 | describe("toLocaleLowerCase", () => { |
| 483 | it("converts to locale lowercase", () => { |
| 484 | strictEqual(pipe("ABC", S.toLocaleLowerCase()), "abc") |
| 485 | }) |
| 486 | }) |
| 487 | |
| 488 | describe("toLocaleUpperCase", () => { |
| 489 | it("converts to locale uppercase", () => { |
| 490 | strictEqual(pipe("abc", S.toLocaleUpperCase()), "ABC") |
| 491 | }) |
| 492 | }) |
| 493 | |
| 494 | describe("takeLeft", () => { |
| 495 | it("takes n characters from the start (data-first)", () => { |
| 496 | strictEqual(S.takeLeft("Hello World", 5), "Hello") |
| 497 | strictEqual(S.takeLeft("abc", 1.5), "a") |
| 498 | }) |
| 499 | |
| 500 | it("takes n characters from the start (data-last)", () => { |
| 501 | strictEqual(pipe("Hello World", S.takeLeft(5)), "Hello") |
| 502 | }) |
| 503 | |
| 504 | it("returns whole string if n > length", () => { |
| 505 | strictEqual(S.takeLeft("abc", 10), "abc") |
| 506 | }) |
| 507 | |
| 508 | it("returns an empty string for non-positive or NaN counts", () => { |
| 509 | strictEqual(S.takeLeft("abc", 0), "") |
| 510 | strictEqual(S.takeLeft("abc", -1), "") |
| 511 | strictEqual(S.takeLeft("abc", Number.NaN), "") |
| 512 | }) |
| 513 | }) |
| 514 | |
| 515 | describe("takeRight", () => { |
| 516 | it("takes n characters from the end (data-first)", () => { |
| 517 | strictEqual(S.takeRight("Hello World", 5), "World") |
| 518 | strictEqual(S.takeRight("abc", 1.5), "c") |
| 519 | }) |
| 520 | |
| 521 | it("takes n characters from the end (data-last)", () => { |
| 522 | strictEqual(pipe("Hello World", S.takeRight(5)), "World") |
| 523 | }) |
| 524 | |
| 525 | it("returns whole string if n > length", () => { |
| 526 | strictEqual(S.takeRight("abc", 10), "abc") |
| 527 | }) |
| 528 | |
| 529 | it("returns an empty string for non-positive or NaN counts", () => { |
| 530 | strictEqual(S.takeRight("abc", 0), "") |
| 531 | strictEqual(S.takeRight("abc", -1), "") |
| 532 | strictEqual(S.takeRight("abc", Number.NaN), "") |
| 533 | }) |
| 534 | }) |
| 535 | |
| 536 | describe("linesIterator", () => { |
| 537 | it("yields lines without separators", () => { |
| 538 | deepStrictEqual(Array.from(S.linesIterator("hello\nworld\n")), ["hello", "world"]) |
| 539 | }) |
| 540 | |
| 541 | it("handles CRLF", () => { |
| 542 | deepStrictEqual(Array.from(S.linesIterator("a\r\nb\r\n")), ["a", "b"]) |
| 543 | }) |
| 544 | |
| 545 | it("handles single line without newline", () => { |
| 546 | deepStrictEqual(Array.from(S.linesIterator("hello")), ["hello"]) |
| 547 | }) |
| 548 | |
| 549 | it("handles empty string", () => { |
| 550 | deepStrictEqual(Array.from(S.linesIterator("")), []) |
| 551 | }) |
| 552 | }) |
| 553 | |
| 554 | describe("linesWithSeparators", () => { |
| 555 | it("yields lines with separators", () => { |
| 556 | deepStrictEqual(Array.from(S.linesWithSeparators("hello\nworld\n")), ["hello\n", "world\n"]) |
| 557 | }) |
| 558 | |
| 559 | it("handles CRLF", () => { |
| 560 | deepStrictEqual(Array.from(S.linesWithSeparators("a\r\nb\r\n")), ["a\r\n", "b\r\n"]) |
| 561 | }) |
| 562 | |
| 563 | it("handles line without trailing newline", () => { |
| 564 | deepStrictEqual(Array.from(S.linesWithSeparators("hello")), ["hello"]) |
| 565 | }) |
| 566 | }) |
| 567 | |
| 568 | describe("stripMargin", () => { |
| 569 | it("strips | margin", () => { |
| 570 | strictEqual(S.stripMargin(" |hello\n |world"), "hello\nworld") |
| 571 | }) |
| 572 | }) |
| 573 | |
| 574 | describe("stripMarginWith", () => { |
| 575 | it("strips custom margin character (data-first)", () => { |
| 576 | strictEqual(S.stripMarginWith(" #hello\n #world", "#"), "hello\nworld") |
| 577 | }) |
| 578 | |
| 579 | it("strips custom margin character (data-last)", () => { |
| 580 | strictEqual(pipe(" #hello\n #world", S.stripMarginWith("#")), "hello\nworld") |
| 581 | }) |
| 582 | |
| 583 | it("leaves lines without margin unchanged", () => { |
| 584 | strictEqual(S.stripMarginWith("no margin\n |yes", "|"), "no margin\nyes") |
| 585 | }) |
| 586 | }) |
| 587 | |
| 588 | describe("snakeToCamel", () => { |
| 589 | it("converts snake_case to camelCase", () => { |
| 590 | strictEqual(S.snakeToCamel("hello_world"), "helloWorld") |
| 591 | strictEqual(S.snakeToCamel("foo_bar_baz"), "fooBarBaz") |
| 592 | }) |
| 593 | |
| 594 | it("handles single word", () => { |
| 595 | strictEqual(S.snakeToCamel("hello"), "hello") |
| 596 | }) |
| 597 | |
| 598 | it("handles an empty string", () => { |
| 599 | strictEqual(S.snakeToCamel(""), "") |
| 600 | }) |
| 601 | }) |
| 602 | |
| 603 | describe("snakeToPascal", () => { |
| 604 | it("converts snake_case to PascalCase", () => { |
| 605 | strictEqual(S.snakeToPascal("hello_world"), "HelloWorld") |
| 606 | strictEqual(S.snakeToPascal("foo_bar_baz"), "FooBarBaz") |
| 607 | }) |
| 608 | |
| 609 | it("handles an empty string", () => { |
| 610 | strictEqual(S.snakeToPascal(""), "") |
| 611 | }) |
| 612 | }) |
| 613 | |
| 614 | describe("snakeToKebab", () => { |
| 615 | it("converts snake_case to kebab-case", () => { |
| 616 | strictEqual(S.snakeToKebab("hello_world"), "hello-world") |
| 617 | strictEqual(S.snakeToKebab("foo_bar_baz"), "foo-bar-baz") |
| 618 | }) |
| 619 | }) |
| 620 | |
| 621 | describe("camelToSnake", () => { |
| 622 | it("converts camelCase to snake_case", () => { |
| 623 | strictEqual(S.camelToSnake("helloWorld"), "hello_world") |
| 624 | strictEqual(S.camelToSnake("fooBarBaz"), "foo_bar_baz") |
| 625 | }) |
| 626 | }) |
| 627 | |
| 628 | describe("pascalToSnake", () => { |
| 629 | it("converts PascalCase to snake_case", () => { |
| 630 | strictEqual(S.pascalToSnake("HelloWorld"), "hello_world") |
| 631 | strictEqual(S.pascalToSnake("FooBarBaz"), "foo_bar_baz") |
| 632 | }) |
| 633 | }) |
| 634 | |
| 635 | describe("kebabToSnake", () => { |
| 636 | it("converts kebab-case to snake_case", () => { |
| 637 | strictEqual(S.kebabToSnake("hello-world"), "hello_world") |
| 638 | strictEqual(S.kebabToSnake("foo-bar-baz"), "foo_bar_baz") |
| 639 | }) |
| 640 | }) |
| 641 | |
| 642 | describe("noCase", () => { |
| 643 | it("converts to space-separated lowercase by default", () => { |
| 644 | strictEqual(S.noCase("helloWorld"), "hello world") |
| 645 | strictEqual(S.noCase("HelloWorld"), "hello world") |
| 646 | }) |
| 647 | |
| 648 | it("data-last with options", () => { |
| 649 | strictEqual(pipe("helloWorld", S.noCase({ delimiter: "-" })), "hello-world") |
| 650 | }) |
| 651 | |
| 652 | it("uses a custom split regular expression", () => { |
| 653 | strictEqual(S.noCase("ab", { splitRegExp: /([a])([b])/g }), "a b") |
| 654 | }) |
| 655 | |
| 656 | it("uses custom split regular expressions", () => { |
| 657 | strictEqual(S.noCase("abc", { splitRegExp: [/([a])([b])/g, /([b])([c])/g] }), "a b c") |
| 658 | }) |
| 659 | |
| 660 | it("uses a custom strip regular expression", () => { |
| 661 | strictEqual(S.noCase("a_b-c", { stripRegExp: /_/g }), "a b-c") |
| 662 | }) |
| 663 | |
| 664 | it("uses custom strip regular expressions", () => { |
| 665 | strictEqual(S.noCase("a_b-c", { stripRegExp: [/_/g, /-/g] }), "a b c") |
| 666 | }) |
| 667 | |
| 668 | it("handles underscores and hyphens", () => { |
| 669 | strictEqual(S.noCase("hello_world"), "hello world") |
| 670 | strictEqual(S.noCase("hello-world"), "hello world") |
| 671 | }) |
| 672 | |
| 673 | it("splits digit-letter boundaries", () => { |
| 674 | strictEqual(S.noCase("field2value"), "field 2 value") |
| 675 | strictEqual(S.noCase("field2Value"), "field 2 value") |
| 676 | }) |
| 677 | }) |
| 678 | |
| 679 | describe("pascalCase", () => { |
| 680 | it("converts to PascalCase", () => { |
| 681 | strictEqual(S.pascalCase("hello world"), "HelloWorld") |
| 682 | strictEqual(S.pascalCase("hello_world"), "HelloWorld") |
| 683 | strictEqual(S.pascalCase("helloWorld"), "HelloWorld") |
| 684 | }) |
| 685 | |
| 686 | it("does not prefix numeric segments with underscores", () => { |
| 687 | strictEqual(S.pascalCase("foo 2 bar"), "Foo2Bar") |
| 688 | strictEqual(S.pascalCase("api-v2 xml"), "ApiV2Xml") |
| 689 | }) |
| 690 | }) |
| 691 | |
| 692 | describe("camelCase", () => { |
| 693 | it("converts to camelCase", () => { |
| 694 | strictEqual(S.camelCase("hello world"), "helloWorld") |
| 695 | strictEqual(S.camelCase("hello_world"), "helloWorld") |
| 696 | strictEqual(S.camelCase("HelloWorld"), "helloWorld") |
| 697 | }) |
| 698 | |
| 699 | it("does not prefix numeric segments with underscores", () => { |
| 700 | strictEqual(S.camelCase("foo 2 bar"), "foo2Bar") |
| 701 | strictEqual(S.camelCase("api-v2 xml"), "apiV2Xml") |
| 702 | }) |
| 703 | }) |
| 704 | |
| 705 | describe("constantCase", () => { |
| 706 | it("converts to CONSTANT_CASE", () => { |
| 707 | strictEqual(S.constantCase("hello world"), "HELLO_WORLD") |
| 708 | strictEqual(S.constantCase("helloWorld"), "HELLO_WORLD") |
| 709 | strictEqual(S.constantCase("api-v2 xml"), "API_V_2_XML") |
| 710 | }) |
| 711 | }) |
| 712 | |
| 713 | describe("configCase", () => { |
| 714 | it("converts to CONFIG_CASE", () => { |
| 715 | strictEqual(S.configCase("hello world"), "HELLO_WORLD") |
| 716 | strictEqual(S.configCase("helloWorld"), "HELLO_WORLD") |
| 717 | strictEqual(S.configCase("api-v2 xml"), "API_V2_XML") |
| 718 | strictEqual(S.configCase("field2Value"), "FIELD2_VALUE") |
| 719 | }) |
| 720 | }) |
| 721 | |
| 722 | describe("kebabCase", () => { |
| 723 | it("converts to kebab-case", () => { |
| 724 | strictEqual(S.kebabCase("hello world"), "hello-world") |
| 725 | strictEqual(S.kebabCase("helloWorld"), "hello-world") |
| 726 | }) |
| 727 | }) |
| 728 | |
| 729 | describe("snakeCase", () => { |
| 730 | it("converts to snake_case", () => { |
| 731 | strictEqual(S.snakeCase("hello world"), "hello_world") |
| 732 | strictEqual(S.snakeCase("helloWorld"), "hello_world") |
| 733 | }) |
| 734 | }) |
| 735 | |
| 736 | describe("ReducerConcat", () => { |
| 737 | it("combines strings", () => { |
| 738 | strictEqual(S.ReducerConcat.combine("a", "b"), "ab") |
| 739 | }) |
| 740 | |
| 741 | it("identity with initialValue", () => { |
| 742 | strictEqual(S.ReducerConcat.combine("a", S.ReducerConcat.initialValue), "a") |
| 743 | strictEqual(S.ReducerConcat.combine(S.ReducerConcat.initialValue, "a"), "a") |
| 744 | }) |
| 745 | }) |
| 746 | }) |