diff --git a/packages/codemode/test/LICENSE.test262 b/packages/codemode/test/LICENSE.test262 new file mode 100644 index 00000000000..fb7434013ba --- /dev/null +++ b/packages/codemode/test/LICENSE.test262 @@ -0,0 +1,28 @@ +Test262: ECMAScript Test Suite ("Software") is protected by copyright and is being +made available under the "BSD License", included below. This Software may be subject to third party rights (rights +from parties other than Ecma International), including patent rights, and no licenses under such third party rights +are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA +CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://www.ecma-international.org/ipr FOR +INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS*. + +Copyright (C) 2012 Ecma International +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following + disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +* Ecma International Standards hereafter means Ecma International Standards as well as Ecma Technical Reports diff --git a/packages/codemode/test/string-core-test262.test.ts b/packages/codemode/test/string-core-test262.test.ts new file mode 100644 index 00000000000..dc804af8584 --- /dev/null +++ b/packages/codemode/test/string-core-test262.test.ts @@ -0,0 +1,580 @@ +/* + * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75: + * - test/built-ins/String/prototype/toLowerCase/S15.5.4.16_A2_T1.js + * - test/built-ins/String/prototype/toLowerCase/special_casing.js + * - test/built-ins/String/prototype/toLowerCase/special_casing_conditional.js + * - test/built-ins/String/prototype/toLowerCase/Final_Sigma_U180E.js + * - test/built-ins/String/prototype/toLowerCase/supplementary_plane.js + * - test/built-ins/String/prototype/toUpperCase/S15.5.4.18_A2_T1.js + * - test/built-ins/String/prototype/toUpperCase/special_casing.js + * - test/built-ins/String/prototype/toUpperCase/supplementary_plane.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-1.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-2.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-3.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-4.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-5.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-6.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-7.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-8.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-9.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-10.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-11.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-12.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-13.js + * - test/built-ins/String/prototype/trim/15.5.4.20-3-14.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-1.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-2.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-3.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-4.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-5.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-6.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-8.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-10.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-11.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-12.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-13.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-14.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-16.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-18.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-19.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-20.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-21.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-22.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-24.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-27.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-28.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-29.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-30.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-32.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-34.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-35.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-36.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-37.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-38.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-39.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-40.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-41.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-42.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-43.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-44.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-45.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-46.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-47.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-48.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-49.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-50.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-51.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-52.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-53.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-54.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-55.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-56.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-57.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-58.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-59.js + * - test/built-ins/String/prototype/trim/15.5.4.20-4-60.js + * - test/built-ins/String/prototype/trim/u180e.js + * - test/built-ins/String/prototype/trimStart/this-value-whitespace.js + * - test/built-ins/String/prototype/trimStart/this-value-line-terminator.js + * - test/built-ins/String/prototype/trimEnd/this-value-whitespace.js + * - test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js + * - test/built-ins/String/prototype/repeat/repeat-string-n-times.js + * - test/built-ins/String/prototype/repeat/empty-string-returns-empty.js + * - test/built-ins/String/prototype/repeat/count-is-zero-returns-empty-string.js + * - test/built-ins/String/prototype/repeat/count-coerced-to-zero-returns-empty-string.js + * - test/built-ins/String/prototype/padStart/fill-string-empty.js + * - test/built-ins/String/prototype/padStart/normal-operation.js + * - test/built-ins/String/prototype/padStart/fill-string-omitted.js + * - test/built-ins/String/prototype/padStart/max-length-not-greater-than-string.js + * - test/built-ins/String/prototype/padEnd/fill-string-empty.js + * - test/built-ins/String/prototype/padEnd/normal-operation.js + * - test/built-ins/String/prototype/padEnd/fill-string-omitted.js + * - test/built-ins/String/prototype/padEnd/max-length-not-greater-than-string.js + * - test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T4.js + * - test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T7.js + * - test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T8.js + * - test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T1.js + * - test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T2.js + * - test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T3.js + * - test/built-ins/String/prototype/charAt/S9.4_A1.js + * - test/built-ins/String/prototype/charAt/S9.4_A2.js + * - test/built-ins/String/prototype/charAt/pos-rounding.js + * - test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T4.js + * - test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T7.js + * - test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T8.js + * - test/built-ins/String/prototype/charCodeAt/pos-rounding.js + * - test/built-ins/String/prototype/codePointAt/return-single-code-unit.js + * - test/built-ins/String/prototype/codePointAt/return-first-code-unit.js + * - test/built-ins/String/prototype/codePointAt/return-utf16-decode.js + * - test/built-ins/String/prototype/codePointAt/return-code-unit-coerced-position.js + * - test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-less-than-zero.js + * - test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-equal-or-more-than-size.js + * - test/built-ins/String/prototype/at/returns-code-unit.js + * - test/built-ins/String/prototype/at/returns-item.js + * - test/built-ins/String/prototype/at/returns-item-relative-index.js + * - test/built-ins/String/prototype/at/returns-undefined-for-out-of-range-index.js + * - test/built-ins/String/prototype/at/index-non-numeric-argument-tointeger.js + * - test/built-ins/String/prototype/concat/S15.5.4.6_A1_T4.js + * - test/built-ins/String/prototype/toString/string-primitive.js + * - test/built-ins/String/prototype/normalize/return-normalized-string.js + * - test/built-ins/String/prototype/normalize/return-normalized-string-using-default-parameter.js + * - test/built-ins/String/prototype/normalize/form-is-not-valid-throws.js + * - test/built-ins/String/prototype/localeCompare/15.5.4.9_CE.js + * - test/built-ins/String/fromCharCode/S15.5.3.2_A2.js + * - test/built-ins/String/fromCharCode/S15.5.3.2_A3_T1.js + * - test/built-ins/String/fromCharCode/S9.7_A1.js + * - test/built-ins/String/fromCharCode/S9.7_A2.1.js + * - test/built-ins/String/fromCharCode/S9.7_A2.2.js + * - test/built-ins/String/fromCharCode/S9.7_A3.2_T1.js + * - test/built-ins/String/fromCodePoint/arguments-is-empty.js + * - test/built-ins/String/fromCodePoint/return-string-value.js + * - test/built-ins/String/fromCodePoint/argument-is-not-integer.js + * - test/built-ins/String/fromCodePoint/number-is-out-of-range.js + * + * Copyright 2009 the Sputnik authors. All rights reserved. + * Copyright (C) 2009 the Sputnik authors. All rights reserved. + * Copyright (c) 2012 Ecma International. All rights reserved. + * Copyright 2012 Norbert Lindenberg. All rights reserved. + * Copyright 2012 Mozilla Corporation. All rights reserved. + * Copyright 2013 Microsoft Corporation. All rights reserved. + * Copyright (C) 2015 the V8 project authors. All rights reserved. + * Copyright (C) 2015 André Bargull. All rights reserved. + * Copyright (C) 2016 the V8 project authors. All rights reserved. + * Copyright (C) 2016 André Bargull. All rights reserved. + * Copyright (C) 2016 Jordan Harband. All rights reserved. + * Copyright (C) 2016 Mathias Bynens. All rights reserved. + * Copyright (c) 2017 Valerie Young. All rights reserved. + * Copyright (C) 2017 Valerie Young. All rights reserved. + * Copyright (C) 2020 Rick Waldron. All rights reserved. + * Copyright (C) 2022 Richard Gibson. All rights reserved. + * Test262 portions are governed by the BSD license in LICENSE.test262. + */ +import { describe, expect, test } from "bun:test" +import { Effect } from "effect" +import { CodeMode } from "../src/index.js" + +type Argument = string | number | undefined +type Outcome = "undefined" | "length" | "RangeError" +type Assertion = { + label: string + input?: string + args?: ReadonlyArray + expected?: string | number + outcome?: Outcome +} +type Vector = { + path: string + method: string + static?: boolean + assertions: ReadonlyArray +} + +const value = async (code: string) => { + const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} })) + if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`) + return result.value +} + +const literal = (input: Argument) => { + if (input === undefined) return "undefined" + if (typeof input === "string") return JSON.stringify(input) + if (Number.isNaN(input)) return "NaN" + if (input === Infinity) return "Infinity" + if (input === -Infinity) return "-Infinity" + if (Object.is(input, -0)) return "-0" + return JSON.stringify(input) +} + +const vectors: Array = [] +const add = (path: string, method: string, assertions: ReadonlyArray, staticMethod = false) => { + vectors.push({ path, method, assertions, static: staticMethod }) +} +const assertion = (label: string, input: string, expected: string | number, args: ReadonlyArray = []) => ({ + label, + input, + args, + expected, +}) + +add("test/built-ins/String/prototype/toLowerCase/S15.5.4.16_A2_T1.js", "toLowerCase", [ + assertion("#1 direct value", "Hello, WoRlD!", "hello, world!"), + assertion("#2 String value", "Hello, WoRlD!", "hello, world!"), +]) +add("test/built-ins/String/prototype/toLowerCase/special_casing.js", "toLowerCase", [ + assertion( + "103 SpecialCasing mappings", + "\u00DF\u0130\uFB00\uFB01\uFB02\uFB03\uFB04\uFB05\uFB06\u0587\uFB13\uFB14\uFB15\uFB16\uFB17\u0149\u0390\u03B0\u01F0\u1E96\u1E97\u1E98\u1E99\u1E9A\u1F50\u1F52\u1F54\u1F56\u1FB6\u1FC6\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2\u1FE3\u1FE4\u1FE6\u1FE7\u1FF6\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F88\u1F89\u1F8A\u1F8B\u1F8C\u1F8D\u1F8E\u1F8F\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1F98\u1F99\u1F9A\u1F9B\u1F9C\u1F9D\u1F9E\u1F9F\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FA8\u1FA9\u1FAA\u1FAB\u1FAC\u1FAD\u1FAE\u1FAF\u1FB3\u1FBC\u1FC3\u1FCC\u1FF3\u1FFC\u1FB2\u1FB4\u1FC2\u1FC4\u1FF2\u1FF4\u1FB7\u1FC7\u1FF7", + "\u00DF\u0069\u0307\uFB00\uFB01\uFB02\uFB03\uFB04\uFB05\uFB06\u0587\uFB13\uFB14\uFB15\uFB16\uFB17\u0149\u0390\u03B0\u01F0\u1E96\u1E97\u1E98\u1E99\u1E9A\u1F50\u1F52\u1F54\u1F56\u1FB6\u1FC6\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2\u1FE3\u1FE4\u1FE6\u1FE7\u1FF6\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FB3\u1FB3\u1FC3\u1FC3\u1FF3\u1FF3\u1FB2\u1FB4\u1FC2\u1FC4\u1FF2\u1FF4\u1FB7\u1FC7\u1FF7", + ), +]) +add("test/built-ins/String/prototype/toLowerCase/special_casing_conditional.js", "toLowerCase", [ + assertion("single sigma", "\u03A3", "\u03C3"), + assertion("preceded by cased", "A\u03A3", "a\u03C2"), + assertion("preceded by supplementary cased", "\uD835\uDCA2\u03A3", "\uD835\uDCA2\u03C2"), + assertion("preceded by full stop", "A.\u03A3", "a.\u03C2"), + assertion("preceded by soft hyphen", "A\u00AD\u03A3", "a\u00AD\u03C2"), + assertion("preceded by combining mark", "A\uD834\uDE42\u03A3", "a\uD834\uDE42\u03C2"), + assertion("preceded by uncased combining mark", "\u0345\u03A3", "\u0345\u03C3"), + assertion("preceded by cased and combining mark", "\u0391\u0345\u03A3", "\u03B1\u0345\u03C2"), + assertion("followed by cased", "A\u03A3B", "a\u03C3b"), + assertion("followed by supplementary cased", "A\u03A3\uD835\uDCA2", "a\u03C3\uD835\uDCA2"), + assertion("followed by full stop and cased", "A\u03A3.b", "a\u03C3.b"), + assertion("followed by soft hyphen and cased", "A\u03A3\u00ADB", "a\u03C3\u00ADb"), + assertion("followed by combining mark and cased", "A\u03A3\uD834\uDE42B", "a\u03C3\uD834\uDE42b"), + assertion("followed by uncased combining mark", "A\u03A3\u0345", "a\u03C2\u0345"), + assertion("followed by combining mark and cased Greek", "A\u03A3\u0345\u0391", "a\u03C3\u0345\u03B1"), +]) +add("test/built-ins/String/prototype/toLowerCase/Final_Sigma_U180E.js", "toLowerCase", [ + assertion("preceded by U+180E", "A\u180E\u03A3", "a\u180E\u03C2"), + assertion("preceded by U+180E and followed by cased", "A\u180E\u03A3B", "a\u180E\u03C3b"), + assertion("followed by U+180E", "A\u03A3\u180E", "a\u03C2\u180E"), + assertion("followed by U+180E and cased", "A\u03A3\u180EB", "a\u03C3\u180Eb"), + assertion("surrounded by U+180E", "A\u180E\u03A3\u180E", "a\u180E\u03C2\u180E"), + assertion("surrounded by U+180E and followed by cased", "A\u180E\u03A3\u180EB", "a\u180E\u03C3\u180Eb"), +]) +add("test/built-ins/String/prototype/toLowerCase/supplementary_plane.js", "toLowerCase", [ + assertion( + "40 Deseret mappings", + "\uD801\uDC00\uD801\uDC01\uD801\uDC02\uD801\uDC03\uD801\uDC04\uD801\uDC05\uD801\uDC06\uD801\uDC07\uD801\uDC08\uD801\uDC09\uD801\uDC0A\uD801\uDC0B\uD801\uDC0C\uD801\uDC0D\uD801\uDC0E\uD801\uDC0F\uD801\uDC10\uD801\uDC11\uD801\uDC12\uD801\uDC13\uD801\uDC14\uD801\uDC15\uD801\uDC16\uD801\uDC17\uD801\uDC18\uD801\uDC19\uD801\uDC1A\uD801\uDC1B\uD801\uDC1C\uD801\uDC1D\uD801\uDC1E\uD801\uDC1F\uD801\uDC20\uD801\uDC21\uD801\uDC22\uD801\uDC23\uD801\uDC24\uD801\uDC25\uD801\uDC26\uD801\uDC27", + "\uD801\uDC28\uD801\uDC29\uD801\uDC2A\uD801\uDC2B\uD801\uDC2C\uD801\uDC2D\uD801\uDC2E\uD801\uDC2F\uD801\uDC30\uD801\uDC31\uD801\uDC32\uD801\uDC33\uD801\uDC34\uD801\uDC35\uD801\uDC36\uD801\uDC37\uD801\uDC38\uD801\uDC39\uD801\uDC3A\uD801\uDC3B\uD801\uDC3C\uD801\uDC3D\uD801\uDC3E\uD801\uDC3F\uD801\uDC40\uD801\uDC41\uD801\uDC42\uD801\uDC43\uD801\uDC44\uD801\uDC45\uD801\uDC46\uD801\uDC47\uD801\uDC48\uD801\uDC49\uD801\uDC4A\uD801\uDC4B\uD801\uDC4C\uD801\uDC4D\uD801\uDC4E\uD801\uDC4F", + ), +]) +add("test/built-ins/String/prototype/toUpperCase/S15.5.4.18_A2_T1.js", "toUpperCase", [ + assertion("#1 direct value", "Hello, WoRlD!", "HELLO, WORLD!"), + assertion("#2 String value", "Hello, WoRlD!", "HELLO, WORLD!"), +]) +add("test/built-ins/String/prototype/toUpperCase/special_casing.js", "toUpperCase", [ + assertion( + "103 SpecialCasing mappings", + "\u00DF\u0130\uFB00\uFB01\uFB02\uFB03\uFB04\uFB05\uFB06\u0587\uFB13\uFB14\uFB15\uFB16\uFB17\u0149\u0390\u03B0\u01F0\u1E96\u1E97\u1E98\u1E99\u1E9A\u1F50\u1F52\u1F54\u1F56\u1FB6\u1FC6\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2\u1FE3\u1FE4\u1FE6\u1FE7\u1FF6\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85\u1F86\u1F87\u1F88\u1F89\u1F8A\u1F8B\u1F8C\u1F8D\u1F8E\u1F8F\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95\u1F96\u1F97\u1F98\u1F99\u1F9A\u1F9B\u1F9C\u1F9D\u1F9E\u1F9F\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5\u1FA6\u1FA7\u1FA8\u1FA9\u1FAA\u1FAB\u1FAC\u1FAD\u1FAE\u1FAF\u1FB3\u1FBC\u1FC3\u1FCC\u1FF3\u1FFC\u1FB2\u1FB4\u1FC2\u1FC4\u1FF2\u1FF4\u1FB7\u1FC7\u1FF7", + "\u0053\u0053\u0130\u0046\u0046\u0046\u0049\u0046\u004C\u0046\u0046\u0049\u0046\u0046\u004C\u0053\u0054\u0053\u0054\u0535\u0552\u0544\u0546\u0544\u0535\u0544\u053B\u054E\u0546\u0544\u053D\u02BC\u004E\u0399\u0308\u0301\u03A5\u0308\u0301\u004A\u030C\u0048\u0331\u0054\u0308\u0057\u030A\u0059\u030A\u0041\u02BE\u03A5\u0313\u03A5\u0313\u0300\u03A5\u0313\u0301\u03A5\u0313\u0342\u0391\u0342\u0397\u0342\u0399\u0308\u0300\u0399\u0308\u0301\u0399\u0342\u0399\u0308\u0342\u03A5\u0308\u0300\u03A5\u0308\u0301\u03A1\u0313\u03A5\u0342\u03A5\u0308\u0342\u03A9\u0342\u1F08\u0399\u1F09\u0399\u1F0A\u0399\u1F0B\u0399\u1F0C\u0399\u1F0D\u0399\u1F0E\u0399\u1F0F\u0399\u1F08\u0399\u1F09\u0399\u1F0A\u0399\u1F0B\u0399\u1F0C\u0399\u1F0D\u0399\u1F0E\u0399\u1F0F\u0399\u1F28\u0399\u1F29\u0399\u1F2A\u0399\u1F2B\u0399\u1F2C\u0399\u1F2D\u0399\u1F2E\u0399\u1F2F\u0399\u1F28\u0399\u1F29\u0399\u1F2A\u0399\u1F2B\u0399\u1F2C\u0399\u1F2D\u0399\u1F2E\u0399\u1F2F\u0399\u1F68\u0399\u1F69\u0399\u1F6A\u0399\u1F6B\u0399\u1F6C\u0399\u1F6D\u0399\u1F6E\u0399\u1F6F\u0399\u1F68\u0399\u1F69\u0399\u1F6A\u0399\u1F6B\u0399\u1F6C\u0399\u1F6D\u0399\u1F6E\u0399\u1F6F\u0399\u0391\u0399\u0391\u0399\u0397\u0399\u0397\u0399\u03A9\u0399\u03A9\u0399\u1FBA\u0399\u0386\u0399\u1FCA\u0399\u0389\u0399\u1FFA\u0399\u038F\u0399\u0391\u0342\u0399\u0397\u0342\u0399\u03A9\u0342\u0399", + ), +]) +add("test/built-ins/String/prototype/toUpperCase/supplementary_plane.js", "toUpperCase", [ + assertion( + "40 Deseret mappings", + "\uD801\uDC28\uD801\uDC29\uD801\uDC2A\uD801\uDC2B\uD801\uDC2C\uD801\uDC2D\uD801\uDC2E\uD801\uDC2F\uD801\uDC30\uD801\uDC31\uD801\uDC32\uD801\uDC33\uD801\uDC34\uD801\uDC35\uD801\uDC36\uD801\uDC37\uD801\uDC38\uD801\uDC39\uD801\uDC3A\uD801\uDC3B\uD801\uDC3C\uD801\uDC3D\uD801\uDC3E\uD801\uDC3F\uD801\uDC40\uD801\uDC41\uD801\uDC42\uD801\uDC43\uD801\uDC44\uD801\uDC45\uD801\uDC46\uD801\uDC47\uD801\uDC48\uD801\uDC49\uD801\uDC4A\uD801\uDC4B\uD801\uDC4C\uD801\uDC4D\uD801\uDC4E\uD801\uDC4F", + "\uD801\uDC00\uD801\uDC01\uD801\uDC02\uD801\uDC03\uD801\uDC04\uD801\uDC05\uD801\uDC06\uD801\uDC07\uD801\uDC08\uD801\uDC09\uD801\uDC0A\uD801\uDC0B\uD801\uDC0C\uD801\uDC0D\uD801\uDC0E\uD801\uDC0F\uD801\uDC10\uD801\uDC11\uD801\uDC12\uD801\uDC13\uD801\uDC14\uD801\uDC15\uD801\uDC16\uD801\uDC17\uD801\uDC18\uD801\uDC19\uD801\uDC1A\uD801\uDC1B\uD801\uDC1C\uD801\uDC1D\uD801\uDC1E\uD801\uDC1F\uD801\uDC20\uD801\uDC21\uD801\uDC22\uD801\uDC23\uD801\uDC24\uD801\uDC25\uD801\uDC26\uD801\uDC27", + ), +]) + +const whitespace = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF" +const lineTerminators = "\u000A\u000D\u2028\u2029" +const trim = (file: string, input: string, expected: string) => + add(`test/built-ins/String/prototype/trim/${file}`, "trim", [assertion("upstream assertion", input, expected)]) + +trim("15.5.4.20-3-1.js", lineTerminators, "") +trim("15.5.4.20-3-2.js", whitespace, "") +trim("15.5.4.20-3-3.js", whitespace + lineTerminators, "") +trim("15.5.4.20-3-4.js", whitespace + lineTerminators + "abc", "abc") +trim("15.5.4.20-3-5.js", "abc" + whitespace + lineTerminators, "abc") +trim("15.5.4.20-3-6.js", whitespace + lineTerminators + "abc" + whitespace + lineTerminators, "abc") +trim("15.5.4.20-3-7.js", "ab" + whitespace + lineTerminators + "cd", "ab" + whitespace + lineTerminators + "cd") +trim("15.5.4.20-3-8.js", "\0\u0000", "\0\u0000") +trim("15.5.4.20-3-9.js", "\0", "\0") +trim("15.5.4.20-3-10.js", "\u0000", "\u0000") +trim("15.5.4.20-3-11.js", "\0\u0000abc", "\0\u0000abc") +trim("15.5.4.20-3-12.js", "abc\0\u0000", "abc\0\u0000") +trim("15.5.4.20-3-13.js", "\0\u0000abc\0\u0000", "\0\u0000abc\0\u0000") +trim("15.5.4.20-3-14.js", "a\0\u0000bc", "a\0\u0000bc") +trim("15.5.4.20-4-1.js", "\u0009a bc \u0009", "a bc") +trim("15.5.4.20-4-2.js", " \u0009abc \u0009", "abc") +trim("15.5.4.20-4-3.js", "\u0009abc", "abc") +trim("15.5.4.20-4-4.js", "\u000Babc", "abc") +trim("15.5.4.20-4-5.js", "\u000Cabc", "abc") +trim("15.5.4.20-4-6.js", "\u0020abc", "abc") +trim("15.5.4.20-4-8.js", "\u00A0abc", "abc") +trim("15.5.4.20-4-10.js", "\uFEFFabc", "abc") +trim("15.5.4.20-4-11.js", "abc\u0009", "abc") +trim("15.5.4.20-4-12.js", "abc\u000B", "abc") +trim("15.5.4.20-4-13.js", "abc\u000C", "abc") +trim("15.5.4.20-4-14.js", "abc\u0020", "abc") +trim("15.5.4.20-4-16.js", "abc\u00A0", "abc") +trim("15.5.4.20-4-18.js", "abc\uFEFF", "abc") +trim("15.5.4.20-4-19.js", "\u0009abc\u0009", "abc") +trim("15.5.4.20-4-20.js", "\u000Babc\u000B", "abc") +trim("15.5.4.20-4-21.js", "\u000Cabc\u000C", "abc") +trim("15.5.4.20-4-22.js", "\u0020abc\u0020", "abc") +trim("15.5.4.20-4-24.js", "\u00A0abc\u00A0", "abc") +trim("15.5.4.20-4-27.js", "\u0009\u0009", "") +trim("15.5.4.20-4-28.js", "\u000B\u000B", "") +trim("15.5.4.20-4-29.js", "\u000C\u000C", "") +trim("15.5.4.20-4-30.js", "\u0020\u0020", "") +trim("15.5.4.20-4-32.js", "\u00A0\u00A0", "") +trim("15.5.4.20-4-34.js", "\uFEFF\uFEFF", "") +trim("15.5.4.20-4-35.js", "ab\u0009c", "ab\u0009c") +trim("15.5.4.20-4-36.js", "ab\u000Bc", "ab\u000Bc") +trim("15.5.4.20-4-37.js", "ab\u000Cc", "ab\u000Cc") +trim("15.5.4.20-4-38.js", "ab\u0020c", "ab\u0020c") +trim("15.5.4.20-4-39.js", "ab\u0085c", "ab\u0085c") +trim("15.5.4.20-4-40.js", "ab\u00A0c", "ab\u00A0c") +trim("15.5.4.20-4-41.js", "ab\u200Bc", "ab\u200Bc") +trim("15.5.4.20-4-42.js", "ab\uFEFFc", "ab\uFEFFc") +trim("15.5.4.20-4-43.js", "\u000Aabc", "abc") +trim("15.5.4.20-4-44.js", "\u000Dabc", "abc") +trim("15.5.4.20-4-45.js", "\u2028abc", "abc") +trim("15.5.4.20-4-46.js", "\u2029abc", "abc") +trim("15.5.4.20-4-47.js", "abc\u000A", "abc") +trim("15.5.4.20-4-48.js", "abc\u000D", "abc") +trim("15.5.4.20-4-49.js", "abc\u2028", "abc") +trim("15.5.4.20-4-50.js", "abc\u2029", "abc") +trim("15.5.4.20-4-51.js", "\u000Aabc\u000A", "abc") +trim("15.5.4.20-4-52.js", "\u000Dabc\u000D", "abc") +trim("15.5.4.20-4-53.js", "\u2028abc\u2028", "abc") +trim("15.5.4.20-4-54.js", "\u2029abc\u2029", "abc") +trim("15.5.4.20-4-55.js", "\u000A\u000A", "") +trim("15.5.4.20-4-56.js", "\u000D\u000D", "") +trim("15.5.4.20-4-57.js", "\u2028\u2028", "") +trim("15.5.4.20-4-58.js", "\u2029\u2029", "") +trim("15.5.4.20-4-59.js", "\u2029 abc", "abc") +trim("15.5.4.20-4-60.js", " ", "") +add("test/built-ins/String/prototype/trim/u180e.js", "trim", [ + assertion("trailing U+180E", "_\u180E", "_\u180E"), + assertion("only U+180E", "\u180E", "\u180E"), + assertion("leading U+180E", "\u180E_", "\u180E_"), +]) + +const directionalWhitespace = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF" +add("test/built-ins/String/prototype/trimStart/this-value-whitespace.js", "trimStart", [ + assertion("all whitespace", directionalWhitespace + "a" + directionalWhitespace + "b" + directionalWhitespace, "a" + directionalWhitespace + "b" + directionalWhitespace), +]) +add("test/built-ins/String/prototype/trimStart/this-value-line-terminator.js", "trimStart", [ + assertion("all line terminators", lineTerminators + "a" + lineTerminators + "b" + lineTerminators, "a" + lineTerminators + "b" + lineTerminators), +]) +add("test/built-ins/String/prototype/trimEnd/this-value-whitespace.js", "trimEnd", [ + assertion("all whitespace", directionalWhitespace + "a" + directionalWhitespace + "b" + directionalWhitespace, directionalWhitespace + "a" + directionalWhitespace + "b"), +]) +add("test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js", "trimEnd", [ + assertion("all line terminators", lineTerminators + "a" + lineTerminators + "b" + lineTerminators, lineTerminators + "a" + lineTerminators + "b"), +]) +add("test/built-ins/String/prototype/repeat/repeat-string-n-times.js", "repeat", [ + assertion("repeat once", "abc", "abc", [1]), + assertion("repeat three times", "abc", "abcabcabc", [3]), + { label: "repeat 10000 times length", input: ".", args: [10000], expected: 10000, outcome: "length" }, +]) +add("test/built-ins/String/prototype/repeat/empty-string-returns-empty.js", "repeat", [ + assertion("count 1", "", "", [1]), + assertion("count 3", "", "", [3]), + assertion("maximum 32-bit count", "", "", [0xffffffff]), +]) +add("test/built-ins/String/prototype/repeat/count-is-zero-returns-empty-string.js", "repeat", [ + assertion("zero", "foo", "", [0]), +]) +add("test/built-ins/String/prototype/repeat/count-coerced-to-zero-returns-empty-string.js", "repeat", [ + assertion("fraction truncates to zero", "abc", "", [0.9]), +]) +add("test/built-ins/String/prototype/padStart/fill-string-empty.js", "padStart", [assertion("empty fill", "abc", "abc", [5, ""])]) +add("test/built-ins/String/prototype/padStart/normal-operation.js", "padStart", [ + assertion("truncated multi-character fill", "abc", "defdabc", [7, "def"]), + assertion("single-character fill", "abc", "**abc", [5, "*"]), + assertion("truncated surrogate pair", "abc", "\uD83D\uDCA9\uD83Dabc", [6, "\uD83D\uDCA9"]), +]) +add("test/built-ins/String/prototype/padStart/fill-string-omitted.js", "padStart", [ + assertion("omitted fill", "abc", " abc", [5]), + assertion("undefined fill", "abc", " abc", [5, undefined]), +]) +add("test/built-ins/String/prototype/padStart/max-length-not-greater-than-string.js", "padStart", [ + assertion("NaN", "abc", "abc", [NaN, "def"]), + assertion("negative infinity", "abc", "abc", [-Infinity, "def"]), + assertion("zero", "abc", "abc", [0, "def"]), + assertion("negative one", "abc", "abc", [-1, "def"]), + assertion("equal length", "abc", "abc", [3, "def"]), + assertion("fraction truncates", "abc", "abc", [3.9999, "def"]), +]) +add("test/built-ins/String/prototype/padEnd/fill-string-empty.js", "padEnd", [assertion("empty fill", "abc", "abc", [5, ""])]) +add("test/built-ins/String/prototype/padEnd/normal-operation.js", "padEnd", [ + assertion("truncated multi-character fill", "abc", "abcdefd", [7, "def"]), + assertion("single-character fill", "abc", "abc**", [5, "*"]), + assertion("truncated surrogate pair", "abc", "abc\uD83D\uDCA9\uD83D", [6, "\uD83D\uDCA9"]), +]) +add("test/built-ins/String/prototype/padEnd/fill-string-omitted.js", "padEnd", [ + assertion("omitted fill", "abc", "abc ", [5]), + assertion("undefined fill", "abc", "abc ", [5, undefined]), +]) +add("test/built-ins/String/prototype/padEnd/max-length-not-greater-than-string.js", "padEnd", [ + assertion("NaN", "abc", "abc", [NaN, "def"]), + assertion("negative infinity", "abc", "abc", [-Infinity, "def"]), + assertion("zero", "abc", "abc", [0, "def"]), + assertion("negative one", "abc", "abc", [-1, "def"]), + assertion("equal length", "abc", "abc", [3, "def"]), + assertion("fraction truncates", "abc", "abc", [3.9999, "def"]), +]) + +add("test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T4.js", "charAt", [assertion("omitted position", "lego", "l")]) +add("test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T7.js", "charAt", [assertion("undefined position", "lego", "l", [undefined])]) +add("test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T8.js", "charAt", [assertion("undefined position", "42", "4", [undefined])]) +add("test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T1.js", "charAt", ["A", "B", "C", "A", "B", "C"].map((expected, position) => assertion(`position ${position}`, "ABCABC", expected, [position]))) +add("test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T2.js", "charAt", [-2, -1].map((position) => assertion(`position ${position}`, "ABCABC", "", [position]))) +add("test/built-ins/String/prototype/charAt/S15.5.4.4_A4_T3.js", "charAt", [6, 7].map((position) => assertion(`position ${position}`, "ABCABC", "", [position]))) +add("test/built-ins/String/prototype/charAt/S9.4_A1.js", "charAt", [assertion("NaN position", "abc", "a", [NaN])]) +add("test/built-ins/String/prototype/charAt/S9.4_A2.js", "charAt", [ + assertion("positive zero", "abc", "a", [0]), + assertion("negative zero", "abc", "a", [-0]), +]) +add("test/built-ins/String/prototype/charAt/pos-rounding.js", "charAt", [ + assertion("-0.99999", "abc", "a", [-0.99999]), + assertion("-0.00001", "abc", "a", [-0.00001]), + assertion("0.00001", "abc", "a", [0.00001]), + assertion("0.99999", "abc", "a", [0.99999]), + assertion("1.00001", "abc", "b", [1.00001]), + assertion("1.99999", "abc", "b", [1.99999]), +]) + +add("test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T4.js", "charCodeAt", [assertion("omitted position", "smart", 0x73)]) +add("test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T7.js", "charCodeAt", [assertion("undefined position", "lego", 0x6c, [undefined])]) +add("test/built-ins/String/prototype/charCodeAt/S15.5.4.5_A1_T8.js", "charCodeAt", [assertion("undefined position", "42", 0x34, [undefined])]) +add("test/built-ins/String/prototype/charCodeAt/pos-rounding.js", "charCodeAt", [ + assertion("-0.99999", "abc", 0x61, [-0.99999]), + assertion("-0.00001", "abc", 0x61, [-0.00001]), + assertion("0.00001", "abc", 0x61, [0.00001]), + assertion("0.99999", "abc", 0x61, [0.99999]), + assertion("1.00001", "abc", 0x62, [1.00001]), + assertion("1.99999", "abc", 0x62, [1.99999]), +]) + +add("test/built-ins/String/prototype/codePointAt/return-single-code-unit.js", "codePointAt", [ + assertion("a", "abc", 97, [0]), assertion("b", "abc", 98, [1]), assertion("c", "abc", 99, [2]), + assertion("ordinary BMP", "\uAAAA\uBBBB", 0xaaaa, [0]), assertion("before high-surrogate range", "\uD7FF\uAAAA", 0xd7ff, [0]), + assertion("low surrogate", "\uDC00\uAAAA", 0xdc00, [0]), assertion("trailing D800", "123\uD800", 0xd800, [3]), + assertion("trailing DAAA", "123\uDAAA", 0xdaaa, [3]), assertion("trailing DBFF", "123\uDBFF", 0xdbff, [3]), +]) +add("test/built-ins/String/prototype/codePointAt/return-first-code-unit.js", "codePointAt", [ + assertion("D800 before DBFF", "\uD800\uDBFF", 0xd800, [0]), assertion("D800 before E000", "\uD800\uE000", 0xd800, [0]), + assertion("DAAA before DBFF", "\uDAAA\uDBFF", 0xdaaa, [0]), assertion("DAAA before E000", "\uDAAA\uE000", 0xdaaa, [0]), + assertion("DBFF before DBFF", "\uDBFF\uDBFF", 0xdbff, [0]), assertion("DBFF before E000", "\uDBFF\uE000", 0xdbff, [0]), + assertion("D800 before NUL", "\uD800\u0000", 0xd800, [0]), assertion("D800 before FFFF", "\uD800\uFFFF", 0xd800, [0]), + assertion("DAAA before NUL", "\uDAAA\u0000", 0xdaaa, [0]), assertion("DAAA before FFFF", "\uDAAA\uFFFF", 0xdaaa, [0]), + assertion("DBFF before FFFF", "\uDBFF\uFFFF", 0xdbff, [0]), +]) +add("test/built-ins/String/prototype/codePointAt/return-utf16-decode.js", "codePointAt", [ + assertion("U+10000", "\uD800\uDC00", 65536, [0]), assertion("U+101D0", "\uD800\uDDD0", 66000, [0]), + assertion("U+103FF", "\uD800\uDFFF", 66559, [0]), assertion("U+BA800", "\uDAAA\uDC00", 763904, [0]), + assertion("U+BA9D0", "\uDAAA\uDDD0", 764368, [0]), assertion("U+BABFF", "\uDAAA\uDFFF", 764927, [0]), + assertion("U+10FC00", "\uDBFF\uDC00", 1113088, [0]), assertion("U+10FDD0", "\uDBFF\uDDD0", 1113552, [0]), + assertion("U+10FFFF", "\uDBFF\uDFFF", 1114111, [0]), +]) +add("test/built-ins/String/prototype/codePointAt/return-code-unit-coerced-position.js", "codePointAt", [ + assertion("NaN", "\uD800\uDC00", 65536, [NaN]), assertion("undefined", "\uD800\uDC00", 65536, [undefined]), +]) +add("test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-less-than-zero.js", "codePointAt", [ + { label: "negative one", input: "abc", args: [-1], outcome: "undefined" }, + { label: "negative infinity", input: "abc", args: [-Infinity], outcome: "undefined" }, +]) +add("test/built-ins/String/prototype/codePointAt/returns-undefined-on-position-equal-or-more-than-size.js", "codePointAt", [ + { label: "equal to size", input: "abc", args: [3], outcome: "undefined" }, + { label: "greater than size", input: "abc", args: [4], outcome: "undefined" }, + { label: "positive infinity", input: "abc", args: [Infinity], outcome: "undefined" }, +]) + +add("test/built-ins/String/prototype/at/returns-code-unit.js", "at", [ + assertion("position 0", "12\uD80034", "1", [0]), assertion("position 1", "12\uD80034", "2", [1]), + assertion("unpaired surrogate", "12\uD80034", "\uD800", [2]), assertion("position 3", "12\uD80034", "3", [3]), + assertion("position 4", "12\uD80034", "4", [4]), +]) +add("test/built-ins/String/prototype/at/returns-item.js", "at", ["1", "2", "3", "4", "5"].map((expected, position) => assertion(`position ${position}`, "12345", expected, [position]))) +add("test/built-ins/String/prototype/at/returns-item-relative-index.js", "at", [ + assertion("zero", "12345", "1", [0]), assertion("negative one", "12345", "5", [-1]), + assertion("negative three", "12345", "3", [-3]), assertion("negative four", "12345", "2", [-4]), +]) +add("test/built-ins/String/prototype/at/returns-undefined-for-out-of-range-index.js", "at", [-2, 0, 1].map((position) => ({ label: `position ${position}`, input: "", args: [position], outcome: "undefined" }))) +add("test/built-ins/String/prototype/at/index-non-numeric-argument-tointeger.js", "at", [assertion("undefined", "01", "0", [undefined])]) + +add("test/built-ins/String/prototype/concat/S15.5.4.6_A1_T4.js", "concat", [assertion("no arguments", "lego", "lego")]) +add("test/built-ins/String/prototype/toString/string-primitive.js", "toString", [ + assertion("empty string", "", ""), assertion("non-empty string", "str", "str"), +]) + +add("test/built-ins/String/prototype/normalize/return-normalized-string.js", "normalize", [ + assertion("NFC short", "\u1E9B\u0323", "\u1E9B\u0323", ["NFC"]), + assertion("NFD short", "\u1E9B\u0323", "\u017F\u0323\u0307", ["NFD"]), + assertion("NFKC short", "\u1E9B\u0323", "\u1E69", ["NFKC"]), + assertion("NFKD short", "\u1E9B\u0323", "\u0073\u0323\u0307", ["NFKD"]), + assertion("NFC long", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFC"]), + assertion("NFD long", "\u00C5\u2ADC\u0958\u2126\u0344", "A\u030A\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFD"]), + assertion("NFKC long", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFKC"]), + assertion("NFKD long", "\u00C5\u2ADC\u0958\u2126\u0344", "A\u030A\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", ["NFKD"]), +]) +add("test/built-ins/String/prototype/normalize/return-normalized-string-using-default-parameter.js", "normalize", [ + assertion("omitted", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301"), + assertion("undefined", "\u00C5\u2ADC\u0958\u2126\u0344", "\xC5\u2ADD\u0338\u0915\u093C\u03A9\u0308\u0301", [undefined]), +]) +add("test/built-ins/String/prototype/normalize/form-is-not-valid-throws.js", "normalize", [ + { label: "bar", input: "foo", args: ["bar"], outcome: "RangeError" }, + { label: "NFC1", input: "foo", args: ["NFC1"], outcome: "RangeError" }, +]) + +add("test/built-ins/String/prototype/localeCompare/15.5.4.9_CE.js", "localeCompare", [ + assertion("D70", "o\u0308", 0, ["ö"]), assertion("reordered diaeresis", "ä\u0323", 0, ["a\u0323\u0308"]), + assertion("reordered marks", "a\u0308\u0323", 0, ["a\u0323\u0308"]), assertion("precomposed dot below", "ạ\u0308", 0, ["a\u0323\u0308"]), + assertion("breve after diaeresis", "ä\u0306", 0, ["a\u0308\u0306"]), assertion("diaeresis after breve", "ă\u0308", 0, ["a\u0306\u0308"]), + assertion("Hangul", "\u1111\u1171\u11B6", 0, ["퓛"]), assertion("angstrom compatibility", "Å", 0, ["Å"]), + assertion("angstrom decomposed", "Å", 0, ["A\u030A"]), assertion("reordered horn and dot", "x\u031B\u0323", 0, ["x\u0323\u031B"]), + assertion("Vietnamese precomposed 1", "ự", 0, ["ụ\u031B"]), assertion("Vietnamese decomposed", "ự", 0, ["u\u031B\u0323"]), + assertion("Vietnamese precomposed 2", "ự", 0, ["ư\u0323"]), assertion("Vietnamese reordered", "ự", 0, ["u\u0323\u031B"]), + assertion("cedilla", "Ç", 0, ["C\u0327"]), assertion("q reordered", "q\u0307\u0323", 0, ["q\u0323\u0307"]), + assertion("Hangul syllable", "가", 0, ["\u1100\u1161"]), assertion("ohm", "Ω", 0, ["Ω"]), + assertion("angstrom", "Å", 0, ["A\u030A"]), assertion("circumflex", "ô", 0, ["o\u0302"]), + assertion("s with marks", "ṩ", 0, ["s\u0323\u0307"]), assertion("d composed plus dot", "ḋ\u0323", 0, ["d\u0323\u0307"]), + assertion("d two precompositions", "ḋ\u0323", 0, ["ḍ\u0307"]), +]) + +add("test/built-ins/String/fromCharCode/S15.5.3.2_A2.js", "fromCharCode", [{ label: "no arguments", expected: "" }], true) +add("test/built-ins/String/fromCharCode/S15.5.3.2_A3_T1.js", "fromCharCode", [{ label: "ABBA", args: [65, 66, 66, 65], expected: "ABBA" }], true) +add("test/built-ins/String/fromCharCode/S9.7_A1.js", "fromCharCode", [ + { label: "NaN", args: [NaN], expected: 0 }, { label: "zero", args: [0], expected: 0 }, { label: "negative zero", args: [-0], expected: 0 }, + { label: "positive infinity", args: [Infinity], expected: 0 }, { label: "negative infinity", args: [-Infinity], expected: 0 }, +], true) +add("test/built-ins/String/fromCharCode/S9.7_A2.1.js", "fromCharCode", [ + [0, 0], [1, 1], [-1, 65535], [65535, 65535], [65534, 65534], [65536, 0], [4294967295, 65535], [4294967294, 65534], [4294967296, 0], +].map(([input, expected]) => ({ label: String(input), args: [input!], expected })), true) +add("test/built-ins/String/fromCharCode/S9.7_A2.2.js", "fromCharCode", [ + [-32767, 32769], [-32768, 32768], [-32769, 32767], [-65535, 1], [-65536, 0], [-65537, 65535], [65535, 65535], [65536, 0], [65537, 1], [131071, 65535], [131072, 0], [131073, 1], +].map(([input, expected]) => ({ label: String(input), args: [input!], expected })), true) +add("test/built-ins/String/fromCharCode/S9.7_A3.2_T1.js", "fromCharCode", [ + { label: "positive fraction", args: [1.2345], expected: 1 }, { label: "negative fraction", args: [-5.4321], expected: 65531 }, +], true) + +add("test/built-ins/String/fromCodePoint/arguments-is-empty.js", "fromCodePoint", [{ label: "no arguments", expected: "" }], true) +add("test/built-ins/String/fromCodePoint/return-string-value.js", "fromCodePoint", [ + { label: "NUL", args: [0], expected: "\x00" }, { label: "asterisk", args: [42], expected: "*" }, + { label: "AZ", args: [65, 90], expected: "AZ" }, { label: "Cyrillic", args: [0x404], expected: "\u0404" }, + { label: "hex supplementary", args: [0x2f804], expected: "\uD87E\uDC04" }, { label: "decimal supplementary", args: [194564], expected: "\uD87E\uDC04" }, + { label: "mixed supplementary", args: [0x1d306, 0x61, 0x1d307], expected: "\uD834\uDF06a\uD834\uDF07" }, + { label: "maximum code point", args: [1114111], expected: "\uDBFF\uDFFF" }, +], true) +add("test/built-ins/String/fromCodePoint/argument-is-not-integer.js", "fromCodePoint", [ + { label: "fraction", args: [3.14], outcome: "RangeError" }, { label: "fraction after valid", args: [42, 3.14], outcome: "RangeError" }, +], true) +add("test/built-ins/String/fromCodePoint/number-is-out-of-range.js", "fromCodePoint", [ + { label: "negative one", args: [-1], outcome: "RangeError" }, { label: "negative after valid", args: [1, -1], outcome: "RangeError" }, + { label: "above maximum", args: [1114112], outcome: "RangeError" }, { label: "infinity", args: [Infinity], outcome: "RangeError" }, +], true) + +describe("Test262-adapted core String behavior", () => { + for (const vector of vectors) { + test(vector.path, async () => { + const results = vector.assertions.map((item) => { + const args = (item.args ?? []).map(literal).join(", ") + const expression = vector.static + ? `String.${vector.method}(${args})` + : `${JSON.stringify(item.input)}.${vector.method}(${args})` + const observed = vector.static && vector.method === "fromCharCode" && typeof item.expected === "number" + ? `${expression}.charCodeAt(0)` + : expression + const checked = item.outcome === "undefined" + ? `${observed} === undefined` + : item.outcome === "length" + ? `${observed}.length` + : item.outcome === "RangeError" + ? `(() => { try { ${observed}; return false } catch (error) { return error instanceof RangeError } })()` + : observed + return `{ label: ${JSON.stringify(item.label)}, value: ${checked} }` + }) + const expected = vector.assertions.map((item) => ({ + label: item.label, + value: item.outcome === undefined || item.outcome === "length" ? item.expected! : true, + })) + expect(await value(`return [${results.join(",")}]`)).toEqual(expected) + }) + } +}) diff --git a/packages/codemode/test/string-regexp-test262.test.ts b/packages/codemode/test/string-regexp-test262.test.ts new file mode 100644 index 00000000000..80c89082299 --- /dev/null +++ b/packages/codemode/test/string-regexp-test262.test.ts @@ -0,0 +1,625 @@ +/* + * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75: + * - test/built-ins/String/prototype/split/separator-regexp.js + * - test/built-ins/String/prototype/split/arguments-are-regexp-s-and-3-and-instance-is-string-a-b-c-de-f.js + * - test/built-ins/String/prototype/split/argument-is-regexp-s-and-instance-is-string-a-b-c-de-f.js + * - test/built-ins/String/prototype/split/argument-is-regexp-d-and-instance-is-string-dfe23iu-34-65.js + * - test/built-ins/String/prototype/split/argument-is-regexp-reg-exp-d-and-instance-is-string-dfe23iu-34-65.js + * - test/built-ins/String/prototype/split/argument-is-regexp-a-z-and-instance-is-string-abc.js + * - test/built-ins/String/prototype/split/argument-is-reg-exp-a-z-and-instance-is-string-abc.js + * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-undefined-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-0-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-1-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-2-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-3-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-regexp-l-and-4-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/argument-is-regexp-l-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/argument-is-new-reg-exp-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-0-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-1-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-2-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-3-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-4-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-undefined-and-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-2-instance-is-string-one-two-three-four-five.js + * - test/built-ins/String/prototype/split/separator-regexp-comma-instance-is-string-one-1-two-2-four-4.js + * - test/built-ins/String/prototype/split/argument-is-regexp-x-and-instance-is-string-a-b-c-de-f.js + * - test/built-ins/String/prototype/replace/regexp-capture-by-index.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A1_T17.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T1.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T2.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T3.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T4.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T5.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T6.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T7.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T8.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T9.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A2_T10.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A3_T1.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A3_T2.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A3_T3.js + * - test/built-ins/String/prototype/replace/S15.5.4.11_A5_T1.js + * - test/built-ins/String/prototype/replaceAll/searchValue-replacer-RegExp-call.js + * - test/built-ins/String/prototype/replaceAll/searchValue-empty-string.js + * - test/built-ins/String/prototype/replaceAll/searchValue-empty-string-this-empty-string.js + * - test/built-ins/String/prototype/replaceAll/replaceValue-value-replaces-string.js + * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024.js + * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0024.js + * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0026.js + * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0060.js + * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0027.js + * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024N.js + * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024NN.js + * - test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x003C.js + * - test/built-ins/String/prototype/match/S15.5.4.10_A1_T14.js + * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T2.js + * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T3.js + * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T4.js + * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T5.js + * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T6.js + * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T7.js + * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T8.js + * - test/built-ins/String/prototype/match/S15.5.4.10_A2_T12.js + * - test/built-ins/String/prototype/matchAll/regexp-prototype-matchAll-v-u-flag.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A1_T14.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T1.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T2.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T3.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T4.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T5.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T6.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A2_T7.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A3_T1.js + * - test/built-ins/String/prototype/search/S15.5.4.12_A3_T2.js + * + * Copyright 2009 the Sputnik authors. All rights reserved. + * Copyright (C) 2019 Leo Balter. All rights reserved. + * Copyright (C) 2020 Rick Waldron. All rights reserved. + * Copyright (C) 2023 Richard Gibson. All rights reserved. + * Copyright (C) 2024 Tan Meng. All rights reserved. + * Test262 portions are governed by the BSD license in LICENSE.test262. + */ +import { describe, expect, test } from "bun:test" +import { Effect } from "effect" +import { CodeMode } from "../src/index.js" + +type Vector = { + readonly path: string + readonly code: string + readonly expected: CodeMode.DataValue +} + +const value = async (code: string) => { + const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} })) + if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`) + return result.value +} + +const run = (name: string, vectors: ReadonlyArray) => { + describe(name, () => { + for (const vector of vectors) { + test(vector.path, async () => { + expect(await value(vector.code)).toEqual(vector.expected) + }) + } + }) +} + +run("Test262-adapted regexp split behavior", [ + { + path: "test/built-ins/String/prototype/split/separator-regexp.js", + code: ` + return [ + "x".split(/^/), "x".split(/$/), "x".split(/.?/), "x".split(/.*/), "x".split(/.+/), + "x".split(/.*?/), "x".split(/.{1}/), "x".split(/.{1,}/), "x".split(/.{1,2}/), + "x".split(/()/), "x".split(/./), "x".split(/(?:)/), "x".split(/(...)/), + "x".split(/(|)/), "x".split(/[]/), "x".split(/[^]/), "x".split(/[.-.]/), + "x".split(/\\0/), "x".split(/\\b/), "x".split(/\\B/), "x".split(/\\d/), + "x".split(/\\D/), "x".split(/\\n/), "x".split(/\\r/), "x".split(/\\s/), + "x".split(/\\S/), "x".split(/\\v/), "x".split(/\\w/), "x".split(/\\W/), + ] + `, + expected: [ + ["x"], ["x"], ["", ""], ["", ""], ["", ""], ["x"], ["", ""], ["", ""], ["", ""], + ["x"], ["", ""], ["x"], ["x"], ["x"], ["x"], ["", ""], ["x"], ["x"], ["x"], + ["x"], ["x"], ["", ""], ["x"], ["x"], ["x"], ["", ""], ["x"], ["", ""], ["x"], + ], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-regexp-s-and-3-and-instance-is-string-a-b-c-de-f.js", + code: `return "a b c de f".split(/\\s/, 3)`, + expected: ["a", "b", "c"], + }, + { + path: "test/built-ins/String/prototype/split/argument-is-regexp-s-and-instance-is-string-a-b-c-de-f.js", + code: `return "a b c de f".split(/\\s/)`, + expected: ["a", "b", "c", "de", "f"], + }, + { + path: "test/built-ins/String/prototype/split/argument-is-regexp-d-and-instance-is-string-dfe23iu-34-65.js", + code: `return "dfe23iu 34 =+65--".split(/\\d+/)`, + expected: ["dfe", "iu ", " =+", "--"], + }, + { + path: "test/built-ins/String/prototype/split/argument-is-regexp-reg-exp-d-and-instance-is-string-dfe23iu-34-65.js", + code: `return "dfe23iu 34 =+65--".split(new RegExp("\\\\d+"))`, + expected: ["dfe", "iu ", " =+", "--"], + }, + { + path: "test/built-ins/String/prototype/split/argument-is-regexp-a-z-and-instance-is-string-abc.js", + code: `return "abc".split(/[a-z]/)`, + expected: ["", "", "", ""], + }, + { + path: "test/built-ins/String/prototype/split/argument-is-reg-exp-a-z-and-instance-is-string-abc.js", + code: `return "abc".split(new RegExp("[a-z]"))`, + expected: ["", "", "", ""], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-undefined-and-instance-is-string-hello.js", + code: `return "hello".split(/l/, undefined)`, + expected: ["he", "", "o"], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-0-and-instance-is-string-hello.js", + code: `return "hello".split(/l/, 0)`, + expected: [], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-1-and-instance-is-string-hello.js", + code: `return "hello".split(/l/, 1)`, + expected: ["he"], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-2-and-instance-is-string-hello.js", + code: `return "hello".split(/l/, 2)`, + expected: ["he", ""], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-3-and-instance-is-string-hello.js", + code: `return "hello".split(/l/, 3)`, + expected: ["he", "", "o"], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-regexp-l-and-4-and-instance-is-string-hello.js", + code: `return "hello".split(/l/, 4)`, + expected: ["he", "", "o"], + }, + { + path: "test/built-ins/String/prototype/split/argument-is-regexp-l-and-instance-is-string-hello.js", + code: `return "hello".split(/l/)`, + expected: ["he", "", "o"], + }, + { + path: "test/built-ins/String/prototype/split/argument-is-new-reg-exp-and-instance-is-string-hello.js", + code: `return "hello".split(new RegExp())`, + expected: ["h", "e", "l", "l", "o"], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-0-and-instance-is-string-hello.js", + code: `return "hello".split(new RegExp(), 0)`, + expected: [], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-1-and-instance-is-string-hello.js", + code: `return "hello".split(new RegExp(), 1)`, + expected: ["h"], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-2-and-instance-is-string-hello.js", + code: `return "hello".split(new RegExp(), 2)`, + expected: ["h", "e"], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-3-and-instance-is-string-hello.js", + code: `return "hello".split(new RegExp(), 3)`, + expected: ["h", "e", "l"], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-4-and-instance-is-string-hello.js", + code: `return "hello".split(new RegExp(), 4)`, + expected: ["h", "e", "l", "l"], + }, + { + path: "test/built-ins/String/prototype/split/arguments-are-new-reg-exp-and-undefined-and-instance-is-string-hello.js", + code: `return "hello".split(new RegExp(), undefined)`, + expected: ["h", "e", "l", "l", "o"], + }, + { + path: "test/built-ins/String/prototype/split/call-split-2-instance-is-string-one-two-three-four-five.js", + code: `return "one two three four five".split(/ /, 2)`, + expected: ["one", "two"], + }, + { + path: "test/built-ins/String/prototype/split/separator-regexp-comma-instance-is-string-one-1-two-2-four-4.js", + code: `return "one-1,two-2,four-4".split(/,/)`, + expected: ["one-1", "two-2", "four-4"], + }, + { + path: "test/built-ins/String/prototype/split/argument-is-regexp-x-and-instance-is-string-a-b-c-de-f.js", + code: `return "a b c de f".split(/X/)`, + expected: ["a b c de f"], + }, +]) + +run("Test262-adapted replace behavior", [ + { + path: "test/built-ins/String/prototype/replace/regexp-capture-by-index.js", + code: ` + const str = "foo-x-bar" + const patterns = ["x", /x/, /(x)/, /(x)($^)?/, /((((((((((x))))))))))/] + const replacements = ["|$0|", "|$00|", "|$000|", "|$1|", "|$01|", "|$010|", "|$2|", "|$02|", "|$020|", "|$10|", "|$100|", "|$20|", "|$200|"] + return replacements.flatMap((replacement) => patterns.map((pattern) => str.replace(pattern, replacement))) + `, + expected: [ + "foo-|$0|-bar", "foo-|$0|-bar", "foo-|$0|-bar", "foo-|$0|-bar", "foo-|$0|-bar", + "foo-|$00|-bar", "foo-|$00|-bar", "foo-|$00|-bar", "foo-|$00|-bar", "foo-|$00|-bar", + "foo-|$000|-bar", "foo-|$000|-bar", "foo-|$000|-bar", "foo-|$000|-bar", "foo-|$000|-bar", + "foo-|$1|-bar", "foo-|$1|-bar", "foo-|x|-bar", "foo-|x|-bar", "foo-|x|-bar", + "foo-|$01|-bar", "foo-|$01|-bar", "foo-|x|-bar", "foo-|x|-bar", "foo-|x|-bar", + "foo-|$010|-bar", "foo-|$010|-bar", "foo-|x0|-bar", "foo-|x0|-bar", "foo-|x0|-bar", + "foo-|$2|-bar", "foo-|$2|-bar", "foo-|$2|-bar", "foo-||-bar", "foo-|x|-bar", + "foo-|$02|-bar", "foo-|$02|-bar", "foo-|$02|-bar", "foo-||-bar", "foo-|x|-bar", + "foo-|$020|-bar", "foo-|$020|-bar", "foo-|$020|-bar", "foo-|0|-bar", "foo-|x0|-bar", + "foo-|$10|-bar", "foo-|$10|-bar", "foo-|x0|-bar", "foo-|x0|-bar", "foo-|x|-bar", + "foo-|$100|-bar", "foo-|$100|-bar", "foo-|x00|-bar", "foo-|x00|-bar", "foo-|x0|-bar", + "foo-|$20|-bar", "foo-|$20|-bar", "foo-|$20|-bar", "foo-|0|-bar", "foo-|x0|-bar", + "foo-|$200|-bar", "foo-|$200|-bar", "foo-|$200|-bar", "foo-|00|-bar", "foo-|x00|-bar", + ], + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A1_T17.js", + code: `return "asdf".replace(new RegExp(undefined, "g"), "1")`, + expected: "1a1s1d1f1", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T1.js", + code: `return "She sells seashells by the seashore.".replace(/sh/g, "sch")`, + expected: "She sells seaschells by the seaschore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T2.js", + code: `return "She sells seashells by the seashore.".replace(/sh/g, "$$sch")`, + expected: "She sells sea$schells by the sea$schore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T3.js", + code: `return "She sells seashells by the seashore.".replace(/sh/g, "$&sch")`, + expected: "She sells seashschells by the seashschore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T4.js", + code: `return "She sells seashells by the seashore.".replace(/sh/g, "$\`sch")`, + expected: "She sells seaShe sells seaschells by the seaShe sells seashells by the seaschore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T5.js", + code: `return "She sells seashells by the seashore.".replace(/sh/g, "$'sch")`, + expected: "She sells seaells by the seashore.schells by the seaore.schore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T6.js", + code: `return "She sells seashells by the seashore.".replace(/sh/, "sch")`, + expected: "She sells seaschells by the seashore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T7.js", + code: `return "She sells seashells by the seashore.".replace(/sh/, "$$sch")`, + expected: "She sells sea$schells by the seashore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T8.js", + code: `return "She sells seashells by the seashore.".replace(/sh/, "$&sch")`, + expected: "She sells seashschells by the seashore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T9.js", + code: `return "She sells seashells by the seashore.".replace(/sh/, "$\`sch")`, + expected: "She sells seaShe sells seaschells by the seashore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A2_T10.js", + code: `return "She sells seashells by the seashore.".replace(/sh/, "$'sch")`, + expected: "She sells seaells by the seashore.schells by the seashore.", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A3_T1.js", + code: `return "uid=31".replace(/(uid=)(\\d+)/, "$1115")`, + expected: "uid=115", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A3_T2.js", + code: `return "uid=31".replace(/(uid=)(\\d+)/, "$1115")`, + expected: "uid=115", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A3_T3.js", + code: `return "uid=31".replace(/(uid=)(\\d+)/, "$11A15")`, + expected: "uid=1A15", + }, + { + path: "test/built-ins/String/prototype/replace/S15.5.4.11_A5_T1.js", + code: `return "aaaaaaaaaa,aaaaaaaaaaaaaaa".replace(/^(a+)\\1*,\\1+$/, "$1")`, + expected: "aaaaa", + }, +]) + +run("Test262-adapted replaceAll behavior", [ + { + path: "test/built-ins/String/prototype/replaceAll/searchValue-replacer-RegExp-call.js", + code: ` + return [ + "abc abc abc".replaceAll(new RegExp("b", "g"), "z"), + "abc abc abc".replaceAll(new RegExp("b", "gy"), "z"), + "abc abc abc".replaceAll(new RegExp("b", "giy"), "z"), + "No Uppercase!".replaceAll(new RegExp("[A-Z]", "g"), ""), + "No Uppercase?".replaceAll(new RegExp("[A-Z]", "gy"), ""), + "NO UPPERCASE!".replaceAll(new RegExp("[A-Z]", "gy"), ""), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "$2-$1"), + "abcabcabcabc".replaceAll(new RegExp("(a(.))", "g"), "$1$2$3"), + "aabacadaeafagahaiajakalamano a azaya".replaceAll(new RegExp("(((((((((((((a(.).).).).).).).).))))))", "g"), "($10)-($12)-($1)"), + "abcba".replaceAll(new RegExp("b", "g"), "$'"), + "abcba".replaceAll(new RegExp("b", "g"), "$\`"), + "abcba".replaceAll(new RegExp("(?b)", "g"), "($)"), + "abcba".replaceAll(new RegExp("(?b)", "g"), "($b)", "g"), "($)"), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$)"), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($)"), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$$$)"), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$$)"), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$&)"), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$1)"), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$\`)"), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($$')"), + "abcabcabcabc".replaceAll(new RegExp("a(?b)(ca)", "g"), "($$)"), + "abcabcabcabc".replaceAll(new RegExp("a(b)(ca)", "g"), "($&)"), + ] + `, + expected: [ + "azc azc azc", "abc abc abc", "abc abc abc", "o ppercase!", "o Uppercase?", " UPPERCASE!", + "ca-bbcca-bbc", "abb$3cabb$3cabb$3cabb$3c", + "(aabaca)-(aaba)-(aabacadaea)f(agahai)-(agah)-(agahaiajak)(alaman)-(alam)-(alamano a )azaya", + "acbacaa", "aacabca", "a(b)c(b)a", "a($)bc($)bc", "(abca)bc(abca)bc", + ], + }, + { + path: "test/built-ins/String/prototype/replaceAll/searchValue-empty-string.js", + code: `return ["aab c \\nx".replaceAll("", "_"), "a".replaceAll("", "_")]`, + expected: ["_a_a_b_ _c_ _ _\n_x_", "_a_"], + }, + { + path: "test/built-ins/String/prototype/replaceAll/searchValue-empty-string-this-empty-string.js", + code: `return "".replaceAll("", "abc")`, + expected: "abc", + }, + { + path: "test/built-ins/String/prototype/replaceAll/replaceValue-value-replaces-string.js", + code: `return ["aaab a a aac".replaceAll("aa", "z"), "aaab a a aac".replaceAll("aa", "a"), "aaab a a aac".replaceAll("a", "a"), "aaab a a aac".replaceAll("a", "z")]`, + expected: ["zab a a zc", "aab a a ac", "aaab a a aac", "zzzb z z zzc"], + }, + { + path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024.js", + code: ` + const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar." + return [str.replaceAll("ninguém", "$"), str.replaceAll("é", "$"), str.replaceAll("é", "$ -"), str.replaceAll("é", "$$$")] + `, + expected: [ + "Ninguém é igual a $. Todo o ser humano é um estranho ímpar.", + "Ningu$m $ igual a ningu$m. Todo o ser humano $ um estranho ímpar.", + "Ningu$ -m $ - igual a ningu$ -m. Todo o ser humano $ - um estranho ímpar.", + "Ningu$$m $$ igual a ningu$$m. Todo o ser humano $$ um estranho ímpar.", + ], + }, + { + path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0024.js", + code: ` + const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar." + return [str.replaceAll("ninguém", "$$"), str.replaceAll("é", "$$"), str.replaceAll("é", "$$ -"), str.replaceAll("é", "$$&"), str.replaceAll("é", "$$$"), str.replaceAll("é", "$$$$")] + `, + expected: [ + "Ninguém é igual a $. Todo o ser humano é um estranho ímpar.", + "Ningu$m $ igual a ningu$m. Todo o ser humano $ um estranho ímpar.", + "Ningu$ -m $ - igual a ningu$ -m. Todo o ser humano $ - um estranho ímpar.", + "Ningu$&m $& igual a ningu$&m. Todo o ser humano $& um estranho ímpar.", + "Ningu$$m $$ igual a ningu$$m. Todo o ser humano $$ um estranho ímpar.", + "Ningu$$m $$ igual a ningu$$m. Todo o ser humano $$ um estranho ímpar.", + ], + }, + { + path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0026.js", + code: ` + const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar." + return [str.replaceAll("ninguém", "$&"), str.replaceAll("ninguém", "($&)"), str.replaceAll("é", "($&)"), str.replaceAll("é", "($&) $&")] + `, + expected: [ + "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar.", + "Ninguém é igual a (ninguém). Todo o ser humano é um estranho ímpar.", + "Ningu(é)m (é) igual a ningu(é)m. Todo o ser humano (é) um estranho ímpar.", + "Ningu(é) ém (é) é igual a ningu(é) ém. Todo o ser humano (é) é um estranho ímpar.", + ], + }, + { + path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0060.js", + code: ` + const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar." + return [str.replaceAll("ninguém", "$\`"), str.replaceAll("Ninguém", "$\`"), str.replaceAll("ninguém", "($\`)"), str.replaceAll("é", "($\`)")] + `, + expected: [ + "Ninguém é igual a Ninguém é igual a . Todo o ser humano é um estranho ímpar.", + " é igual a ninguém. Todo o ser humano é um estranho ímpar.", + "Ninguém é igual a (Ninguém é igual a ). Todo o ser humano é um estranho ímpar.", + "Ningu(Ningu)m (Ninguém ) igual a ningu(Ninguém é igual a ningu)m. Todo o ser humano (Ninguém é igual a ninguém. Todo o ser humano ) um estranho ímpar.", + ], + }, + { + path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x0027.js", + code: ` + const str = "Ninguém é igual a ninguém. Todo o ser humano é um estranho ímpar." + return [str.replaceAll("ninguém", "$'"), str.replaceAll(".", "--- $'"), str.replaceAll("é", "($')")] + `, + expected: [ + "Ninguém é igual a . Todo o ser humano é um estranho ímpar.. Todo o ser humano é um estranho ímpar.", + "Ninguém é igual a ninguém--- Todo o ser humano é um estranho ímpar. Todo o ser humano é um estranho ímpar--- ", + "Ningu(m é igual a ninguém. Todo o ser humano é um estranho ímpar.)m ( igual a ninguém. Todo o ser humano é um estranho ímpar.) igual a ningu(m. Todo o ser humano é um estranho ímpar.)m. Todo o ser humano ( um estranho ímpar.) um estranho ímpar.", + ], + }, + { + path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024N.js", + code: ` + const str = "ABC AAA ABC AAA" + return ["$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"].map((replacement) => str.replaceAll("ABC", replacement)) + `, + expected: ["$1 AAA $1 AAA", "$2 AAA $2 AAA", "$3 AAA $3 AAA", "$4 AAA $4 AAA", "$5 AAA $5 AAA", "$6 AAA $6 AAA", "$7 AAA $7 AAA", "$8 AAA $8 AAA", "$9 AAA $9 AAA"], + }, + { + path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024NN.js", + code: ` + const str = "aaaaaaaaaaaaaaaa aaaaaaaa aaaaaaaaaaaaaaaa" + return [str.replaceAll("a", "$11"), str.replaceAll("a", "$29")] + `, + expected: [ + "$11$11$11$11$11$11$11$11$11$11$11$11$11$11$11$11 $11$11$11$11$11$11$11$11 $11$11$11$11$11$11$11$11$11$11$11$11$11$11$11$11", + "$29$29$29$29$29$29$29$29$29$29$29$29$29$29$29$29 $29$29$29$29$29$29$29$29 $29$29$29$29$29$29$29$29$29$29$29$29$29$29$29$29", + ], + }, + { + path: "test/built-ins/String/prototype/replaceAll/getSubstitution-0x0024-0x003C.js", + code: `return "aaaaaaaaaaaaaaaa aaaaaaaa aaaaaaaaaaaaaaaa".replaceAll("a", "$<")`, + expected: "$<$<$<$<$<$<$<$<$<$<$<$<$<$<$<$< $<$<$<$<$<$<$<$< $<$<$<$<$<$<$<$<$<$<$<$<$<$<$<$<", + }, +]) + +run("Test262-adapted match behavior", [ + { + path: "test/built-ins/String/prototype/match/S15.5.4.10_A1_T14.js", + code: `const match = "ABBABABAB77BBAA".match(new RegExp("77")); return [match[0], match.index]`, + expected: ["77", 9], + }, + { + path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T2.js", + code: `return "343443444".match(/34/g)`, + expected: ["34", "34", "34"], + }, + { + path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T3.js", + code: `return "123456abcde7890".match(/\\d{1}/g)`, + expected: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], + }, + { + path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T4.js", + code: `return "123456abcde7890".match(/\\d{2}/g)`, + expected: ["12", "34", "56", "78", "90"], + }, + { + path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T5.js", + code: `return "123456abcde7890".match(/\\D{2}/g)`, + expected: ["ab", "cd"], + }, + { + path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T6.js", + code: `const match = "Boston, Mass. 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/); return [match[0], match[1], match[2] === undefined, match.length, match.index]`, + expected: ["02134", "02134", true, 3, 14], + }, + { + path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T7.js", + code: `return "Boston, Mass. 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/g)`, + expected: ["02134"], + }, + { + path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T8.js", + code: `const match = "Boston, MA 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/); return [match[0], match[1], match[2] === undefined, match.length, match.index]`, + expected: ["02134", "02134", true, 3, 11], + }, + { + path: "test/built-ins/String/prototype/match/S15.5.4.10_A2_T12.js", + code: `return "Boston, MA 02134".match(/([\\d]{5})([- ]?[\\d]{4})?$/g)`, + expected: ["02134"], + }, +]) + +run("Test262-adapted matchAll behavior", [ + { + path: "test/built-ins/String/prototype/matchAll/regexp-prototype-matchAll-v-u-flag.js", + code: ` + const text = "𠮷a𠮷b𠮷" + const collect = (regex) => { + const matches = text.matchAll(regex) + return matches.map((match) => match[0]).concat(matches.map((match) => match.index)) + } + const empty = text.matchAll(/(?:)/gu) + const complex = "a𠮷b􏿿c".matchAll(/\\P{ASCII}/gu) + return [ + collect(/𠮷/g), + collect(/𠮷/gu), + collect(/\\p{Script=Han}/gu), + collect(/./gu), + empty.map((match) => match[0]).concat(empty.map((match) => match.index)).length, + complex.map((match) => match[0]), + ] + `, + expected: [ + ["𠮷", "𠮷", "𠮷", 0, 3, 6], + ["𠮷", "𠮷", "𠮷", 0, 3, 6], + ["𠮷", "𠮷", "𠮷", 0, 3, 6], + ["𠮷", "a", "𠮷", "b", "𠮷", 0, 2, 3, 5, 6], + 12, + ["𠮷", "􏿿"], + ], + }, +]) + +run("Test262-adapted search behavior", [ + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A1_T14.js", + code: `return "ABBABABAB77BBAA".search(new RegExp("77"))`, + expected: 9, + }, + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T1.js", + code: `return "test string".search("string")`, + expected: 5, + }, + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T2.js", + code: `return "test string".search("String")`, + expected: -1, + }, + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T3.js", + code: `return "test string".search(/String/i)`, + expected: 5, + }, + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T4.js", + code: `return "one two three four five".search(/Four/)`, + expected: -1, + }, + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T5.js", + code: `return "one two three four five".search(/four/)`, + expected: 14, + }, + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T6.js", + code: `return "test string".search("notexist")`, + expected: -1, + }, + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A2_T7.js", + code: `return "test string probe".search("string pro")`, + expected: 5, + }, + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A3_T1.js", + code: `const text = "power of the power of the power of the great sword"; return [text.search(/the/), text.search(/the/g)]`, + expected: [9, 9], + }, + { + path: "test/built-ins/String/prototype/search/S15.5.4.12_A3_T2.js", + code: `const text = "power of the power of the power of the great sword"; return [text.search(/of/), text.search(/of/g)]`, + expected: [6, 6], + }, +]) diff --git a/packages/codemode/test/string-search-test262.test.ts b/packages/codemode/test/string-search-test262.test.ts new file mode 100644 index 00000000000..f332ea16715 --- /dev/null +++ b/packages/codemode/test/string-search-test262.test.ts @@ -0,0 +1,794 @@ +/* + * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75: + * - test/built-ins/String/prototype/split/call-split-l-0-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-l-1-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-l-2-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-l-3-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-l-4-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-l-na-n-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-l-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-ll-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-h-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-hello-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-hellothere-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-o-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-x-instance-is-string-hello.js + * - test/built-ins/String/prototype/split/call-split-x-instance-is-empty-string.js + * - test/built-ins/String/prototype/split/call-split-4-instance-is-string-one-1-two-2-four-4.js + * - test/built-ins/String/prototype/split/call-split-on-instance-is-string-one-1-two-2-four-4.js + * - test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three-four-five.js + * - test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three.js + * - test/built-ins/String/prototype/split/call-split-instance-is-string.js + * - test/built-ins/String/prototype/split/instance-is-string-one-two-three-four-five.js + * - test/built-ins/String/prototype/split/instance-is-string.js + * - test/built-ins/String/prototype/split/separator-colon-instance-is-string-one-1-two-2-four-4.js + * - test/built-ins/String/prototype/split/separator-comma-instance-is-string-one-two-three-four-five.js + * - test/built-ins/String/prototype/split/separator-empty-string-instance-is-string.js + * - test/built-ins/String/prototype/split/call-split-without-arguments-and-instance-is-empty-string.js + * - test/built-ins/String/prototype/split/separator-undef.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A1_T6.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A1_T14.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T1.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T2.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T3.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T4.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T5.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T6.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T7.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T8.js + * - test/built-ins/String/prototype/slice/S15.5.4.13_A2_T9.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A1_T6.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A1_T14.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T1.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T2.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T3.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T4.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T5.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T6.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T7.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T8.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T9.js + * - test/built-ins/String/prototype/substring/S15.5.4.15_A2_T10.js + * - test/annexB/built-ins/String/prototype/substr/start-negative.js + * - test/annexB/built-ins/String/prototype/substr/length-negative.js + * - test/annexB/built-ins/String/prototype/substr/length-positive.js + * - test/annexB/built-ins/String/prototype/substr/length-falsey.js + * - test/annexB/built-ins/String/prototype/substr/length-undef.js + * - test/annexB/built-ins/String/prototype/substr/surrogate-pairs.js + * - test/built-ins/String/prototype/includes/String.prototype.includes_FailMissingLetter.js + * - test/built-ins/String/prototype/includes/String.prototype.includes_SuccessNoLocation.js + * - test/built-ins/String/prototype/includes/String.prototype.includes_FailBadLocation.js + * - test/built-ins/String/prototype/includes/String.prototype.includes_FailLocation.js + * - test/built-ins/String/prototype/includes/String.prototype.includes_Success.js + * - test/built-ins/String/prototype/includes/searchstring-found-with-position.js + * - test/built-ins/String/prototype/includes/searchstring-found-without-position.js + * - test/built-ins/String/prototype/includes/searchstring-not-found-with-position.js + * - test/built-ins/String/prototype/includes/searchstring-not-found-without-position.js + * - test/built-ins/String/prototype/includes/return-false-with-out-of-bounds-position.js + * - test/built-ins/String/prototype/includes/return-true-if-searchstring-is-empty.js + * - test/built-ins/String/prototype/includes/coerced-values-of-position.js + * - test/built-ins/String/prototype/startsWith/searchstring-found-with-position.js + * - test/built-ins/String/prototype/startsWith/searchstring-found-without-position.js + * - test/built-ins/String/prototype/startsWith/searchstring-not-found-with-position.js + * - test/built-ins/String/prototype/startsWith/searchstring-not-found-without-position.js + * - test/built-ins/String/prototype/startsWith/out-of-bounds-position.js + * - test/built-ins/String/prototype/startsWith/return-true-if-searchstring-is-empty.js + * - test/built-ins/String/prototype/startsWith/coerced-values-of-position.js + * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success.js + * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_2.js + * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_3.js + * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_4.js + * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail.js + * - test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail_2.js + * - test/built-ins/String/prototype/endsWith/searchstring-found-with-position.js + * - test/built-ins/String/prototype/endsWith/searchstring-found-without-position.js + * - test/built-ins/String/prototype/endsWith/searchstring-not-found-with-position.js + * - test/built-ins/String/prototype/endsWith/searchstring-not-found-without-position.js + * - test/built-ins/String/prototype/endsWith/return-false-if-search-start-is-less-than-zero.js + * - test/built-ins/String/prototype/endsWith/return-true-if-searchstring-is-empty.js + * - test/built-ins/String/prototype/endsWith/coerced-values-of-position.js + * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T1.js + * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T2.js + * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T3.js + * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T4.js + * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T1.js + * - test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T3.js + * - test/built-ins/String/prototype/indexOf/position-tointeger.js + * - test/built-ins/String/prototype/indexOf/searchstring-tostring.js + * - test/built-ins/String/prototype/lastIndexOf/not-a-substring.js + * + * Copyright 2009 the Sputnik authors. All rights reserved. + * Copyright (c) 2014 Ryan Lewis. All rights reserved. + * Copyright (C) 2015 the V8 project authors. All rights reserved. + * Copyright (C) 2016 the V8 project authors. All rights reserved. + * Copyright (C) 2017 Josh Wolfe. All rights reserved. + * Copyright (C) 2020 Leo Balter. All rights reserved. + * Copyright (C) 2026 Garham Lee. All rights reserved. + * Test262 portions are governed by the BSD license in LICENSE.test262. + */ +import { describe, expect, test } from "bun:test" +import { Effect } from "effect" +import { CodeMode } from "../src/index.js" + +const value = async (code: string) => { + const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} })) + if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`) + return result.value +} + +const cases = [ + { + path: "test/built-ins/String/prototype/split/call-split-l-0-instance-is-string-hello.js", + code: `const result = "hello".split("l", 0); return [result.length, result[0] === undefined]`, + expected: [0, true], + labels: [ + "The value of __split.length is expected to equal the value of __expected.length", + "The value of __split[0] is expected to equal the value of __expected[0]", + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-l-1-instance-is-string-hello.js", + code: `const result = "hello".split("l", 1); return [result.length, result[0]]`, + expected: [1, "he"], + labels: [ + "The value of __split.length is expected to equal the value of __expected.length", + "The value of __split[0] is expected to equal the value of __expected[0]", + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-l-2-instance-is-string-hello.js", + code: `const result = "hello".split("l", 2); return [result.length, result[0], result[1]]`, + expected: [2, "he", ""], + labels: [ + "The value of __split.length is expected to equal the value of __expected.length", + "The value of __split[index] is expected to equal the value of __expected[index]", + "The value of __split[index] is expected to equal the value of __expected[index]", + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-l-3-instance-is-string-hello.js", + code: `const result = "hello".split("l", 3); return [result.length, result[0], result[1], result[2]]`, + expected: [3, "he", "", "o"], + labels: [ + "The value of __split.length is expected to equal the value of __expected.length", + "The value of __split[index] is expected to equal the value of __expected[index]", + "The value of __split[index] is expected to equal the value of __expected[index]", + "The value of __split[index] is expected to equal the value of __expected[index]", + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-l-4-instance-is-string-hello.js", + code: `const result = "hello".split("l", 4); return [result.length, result[0], result[1], result[2]]`, + expected: [3, "he", "", "o"], + labels: [ + "The value of __split.length is expected to equal the value of __expected.length", + "The value of __split[index] is expected to equal the value of __expected[index]", + "The value of __split[index] is expected to equal the value of __expected[index]", + "The value of __split[index] is expected to equal the value of __expected[index]", + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-l-na-n-instance-is-string-hello.js", + code: `const result = "hello".split("l", NaN); return [result.length, result[0] === undefined]`, + expected: [0, true], + labels: [ + "The value of __split.length is expected to equal the value of __expected.length", + "The value of __split[0] is expected to equal the value of __expected[0]", + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-l-instance-is-string-hello.js", + code: `const result = "hello".split("l"); return [result.length, result[0], result[1], result[2]]`, + expected: [3, "he", "", "o"], + labels: [ + "The value of __split.length is 3", + 'The value of __split[0] is "he"', + 'The value of __split[1] is ""', + 'The value of __split[2] is "o"', + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-ll-instance-is-string-hello.js", + code: `const result = "hello".split("ll"); return [result.length, result[0], result[1]]`, + expected: [2, "he", "o"], + labels: ["The value of __split.length is 2", 'The value of __split[0] is "he"', 'The value of __split[1] is "o"'], + }, + { + path: "test/built-ins/String/prototype/split/call-split-h-instance-is-string-hello.js", + code: `const result = "hello".split("h"); return [result.length, result[0], result[1]]`, + expected: [2, "", "ello"], + labels: ["The value of __split.length is 2", 'The value of __split[0] is ""', 'The value of __split[1] is "ello"'], + }, + { + path: "test/built-ins/String/prototype/split/call-split-hello-instance-is-string-hello.js", + code: `const result = "hello".split("hello"); return [result.length, result[0], result[1]]`, + expected: [2, "", ""], + labels: ["The value of __split.length is 2", 'The value of __split[0] is ""', 'The value of __split[1] is ""'], + }, + { + path: "test/built-ins/String/prototype/split/call-split-hellothere-instance-is-string-hello.js", + code: `const result = "hello".split("hellothere"); return [result.length, result[0]]`, + expected: [1, "hello"], + labels: ["The value of __split.length is 1", 'The value of __split[0] is "hello"'], + }, + { + path: "test/built-ins/String/prototype/split/call-split-o-instance-is-string-hello.js", + code: `const result = "hello".split("o"); return [result.length, result[0], result[1]]`, + expected: [2, "hell", ""], + labels: ["The value of __split.length is 2", 'The value of __split[0] is "hell"', 'The value of __split[1] is ""'], + }, + { + path: "test/built-ins/String/prototype/split/call-split-x-instance-is-string-hello.js", + code: `const result = "hello".split("x"); return [result.length, result[0]]`, + expected: [1, "hello"], + labels: ["The value of __split.length is 1", 'The value of __split[0] is "hello"'], + }, + { + path: "test/built-ins/String/prototype/split/call-split-x-instance-is-empty-string.js", + code: `const result = "".split("x"); return [result.length, result[0]]`, + expected: [1, ""], + labels: ["The value of __split.length is 1", 'The value of __split[0] is ""'], + }, + { + path: "test/built-ins/String/prototype/split/call-split-4-instance-is-string-one-1-two-2-four-4.js", + code: `const result = "one-1 two-2 four-4".split("-4"); return [result.length, result[0], result[1]]`, + expected: [2, "one-1 two-2 four", ""], + labels: [ + "The value of __split.length is 2", + 'The value of __split[0] is "one-1 two-2 four"', + 'The value of __split[1] is ""', + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-on-instance-is-string-one-1-two-2-four-4.js", + code: `const result = "one-1 two-2 four-4".split("on"); return [result.length, result[0], result[1]]`, + expected: [2, "", "e-1 two-2 four-4"], + labels: [ + "The value of __split.length is 2", + 'The value of __split[0] is ""', + 'The value of __split[1] is "e-1 two-2 four-4"', + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three-four-five.js", + code: `const result = "one two three four five".split(" "); return [result.length, ...result]`, + expected: [5, "one", "two", "three", "four", "five"], + labels: [ + "The value of __split.length is 5", 'The value of __split[0] is "one"', 'The value of __split[1] is "two"', + 'The value of __split[2] is "three"', 'The value of __split[3] is "four"', 'The value of __split[4] is "five"', + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-instance-is-string-one-two-three.js", + code: `const result = "one two three".split(""); return [result[0], result[1], result[11], result[12]]`, + expected: ["o", "n", "e", "e"], + labels: [ + 'The value of __split[0] is "o"', 'The value of __split[1] is "n"', + 'The value of __split[11] is "e"', 'The value of __split[12] is "e"', + ], + }, + { + path: "test/built-ins/String/prototype/split/call-split-instance-is-string.js", + code: `const result = " ".split(" "); return [result.length, result[0], result[1]]`, + expected: [2, "", ""], + labels: ["The value of __split.length is 2", 'The value of __split[0] is ""', 'The value of __split[1] is ""'], + }, + { + path: "test/built-ins/String/prototype/split/instance-is-string-one-two-three-four-five.js", + code: `const result = "one,two,three,four,five".split(); return [result.length, result[0]]`, + expected: [1, "one,two,three,four,five"], + labels: ["The value of __split.length is 1", 'The value of __split[0] is "one,two,three,four,five"'], + }, + { + path: "test/built-ins/String/prototype/split/instance-is-string.js", + code: `const result = " ".split(); return [result.length, result[0]]`, + expected: [1, " "], + labels: ["The value of __split.length is 1", 'The value of __split[0] is " "'], + }, + { + path: "test/built-ins/String/prototype/split/separator-colon-instance-is-string-one-1-two-2-four-4.js", + code: `const result = "one-1,two-2,four-4".split(":"); return [result.length, result[0]]`, + expected: [1, "one-1,two-2,four-4"], + labels: ["The value of __split.length is 1", 'The value of __split[0] is "one-1,two-2,four-4"'], + }, + { + path: "test/built-ins/String/prototype/split/separator-comma-instance-is-string-one-two-three-four-five.js", + code: `const result = "one,two,three,four,five".split(","); return [result.length, ...result]`, + expected: [5, "one", "two", "three", "four", "five"], + labels: [ + "The value of __split.length is 5", + 'The value of __split[0] is "one"', + 'The value of __split[1] is "two"', + 'The value of __split[2] is "three"', + 'The value of __split[3] is "four"', + 'The value of __split[4] is "five"', + ], + }, + { + path: "test/built-ins/String/prototype/split/separator-empty-string-instance-is-string.js", + code: `const result = " ".split(""); return [result.length, result[0]]`, + expected: [1, " "], + labels: ["The value of __split.length is 1", 'The value of __split[0] is " "'], + }, + { + path: "test/built-ins/String/prototype/split/call-split-without-arguments-and-instance-is-empty-string.js", + code: `const result = "".split(); return [result.length, result[0]]`, + expected: [1, ""], + labels: ["The value of __split.length is 1", 'The value of __split[0] is ""'], + }, + { + path: "test/built-ins/String/prototype/split/separator-undef.js", + code: `const result = "undefined is not a function".split(); return [Array.isArray(result), result.length, result[0]]`, + expected: [true, 1, "undefined is not a function"], + labels: ["implicit separator, result is array", "implicit separator, result.length", "implicit separator, [0] is the same string"], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A1_T6.js", + code: `return ["undefined".slice(undefined, 3)]`, + expected: ["und"], + labels: ['#1: new String("undefined").slice(x,3) === "und"'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A1_T14.js", + code: `return ["report".slice(undefined)]`, + expected: ["report"], + labels: ['#1: "report".slice(function(){}()) === "report"'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T1.js", + code: `return [typeof "this is a string object".slice()]`, + expected: ["string"], + labels: ['#1: typeof __string.slice() === "string"'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T2.js", + code: `return ["this is a string object".slice(NaN, Infinity)]`, + expected: ["this is a string object"], + labels: ['#1: __string.slice(NaN, Infinity) === "this is a string object"'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T3.js", + code: `return ["".slice(1, 0)]`, + expected: [""], + labels: ['#1: __string.slice(1,0) === ""'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T4.js", + code: `return ["this is a string object".slice(Infinity, NaN)]`, + expected: [""], + labels: ['#1: __string.slice(Infinity, NaN) === ""'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T5.js", + code: `return ["this is a string object".slice(Infinity, Infinity)]`, + expected: [""], + labels: ['#1: __string.slice(Infinity, Infinity) === ""'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T6.js", + code: `return ["this is a string object".slice(-0.01, 0)]`, + expected: [""], + labels: ['#1: __string.slice(-0.01,0) === ""'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T7.js", + code: `const text = "this is a string object"; return [text.slice(text.length, text.length)]`, + expected: [""], + labels: ['#1: __string.slice(__string.length, __string.length) === ""'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T8.js", + code: `const text = "this is a string object"; return [text.slice(text.length + 1, 0)]`, + expected: [""], + labels: ['#1: __string.slice(__string.length+1, 0) === ""'], + }, + { + path: "test/built-ins/String/prototype/slice/S15.5.4.13_A2_T9.js", + code: `return ["this is a string object".slice(-Infinity, -Infinity)]`, + expected: [""], + labels: ['#1: __string.slice(-Infinity, -Infinity) === ""'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A1_T6.js", + code: `return ["undefined".substring(undefined, 3)]`, + expected: ["und"], + labels: ['#1: new String("undefined").substring(x,3) === "und"'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A1_T14.js", + code: `return ["report".substring(undefined)]`, + expected: ["report"], + labels: ['#1: "report".substring(function(){}()) === "report"'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T1.js", + code: `return [typeof "this is a string object".substring()]`, + expected: ["string"], + labels: ['#1: typeof __string.substring() === "string"'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T2.js", + code: `return ["this is a string object".substring(NaN, Infinity)]`, + expected: ["this is a string object"], + labels: ['#1: __string.substring(NaN, Infinity) === "this is a string object"'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T3.js", + code: `return ["".substring(1, 0)]`, + expected: [""], + labels: ['#1: __string.substring(1,0) === ""'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T4.js", + code: `return ["this is a string object".substring(Infinity, NaN)]`, + expected: ["this is a string object"], + labels: ['#1: __string.substring(Infinity, NaN) === "this is a string object"'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T5.js", + code: `return ["this is a string object".substring(Infinity, Infinity)]`, + expected: [""], + labels: ['#1: __string.substring(Infinity, Infinity) === ""'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T6.js", + code: `return ["this is a string object".substring(-0.01, 0)]`, + expected: [""], + labels: ['#1: __string.substring(-0.01,0) === ""'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T7.js", + code: `const text = "this is a string object"; return [text.substring(text.length, text.length)]`, + expected: [""], + labels: ['#1: __string.substring(__string.length, __string.length) === ""'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T8.js", + code: `const text = "this is a string object"; return [text.substring(text.length + 1, 0)]`, + expected: ["this is a string object"], + labels: ['#1: __string.substring(__string.length+1, 0) === "this is a string object"'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T9.js", + code: `return ["this is a string object".substring(-Infinity, -Infinity)]`, + expected: [""], + labels: ['#1: __string.substring(-Infinity, -Infinity) === ""'], + }, + { + path: "test/built-ins/String/prototype/substring/S15.5.4.15_A2_T10.js", + code: `return ["this_is_a_string object".substring(0, 8)]`, + expected: ["this_is_"], + labels: ['#1: __string.substring(0,8) === "this_is_"'], + }, + { + path: "test/annexB/built-ins/String/prototype/substr/start-negative.js", + code: `return ["abc".substr(-1), "abc".substr(-2), "abc".substr(-3), "abc".substr(-4), "abc".substr(-1.1)]`, + expected: ["c", "bc", "abc", "abc", "c"], + labels: ["-1", "-2", "-3", "size + intStart < 0", "floating point rounding semantics"], + }, + { + path: "test/annexB/built-ins/String/prototype/substr/length-negative.js", + code: `return [ + "abc".substr(0, -1), "abc".substr(0, -2), "abc".substr(0, -3), "abc".substr(0, -4), + "abc".substr(1, -1), "abc".substr(1, -2), "abc".substr(1, -3), "abc".substr(1, -4), + "abc".substr(2, -1), "abc".substr(2, -2), "abc".substr(2, -3), "abc".substr(2, -4), + "abc".substr(3, -1), "abc".substr(3, -2), "abc".substr(3, -3), "abc".substr(3, -4), + ]`, + expected: ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""], + labels: [ + "0, -1", "0, -2", "0, -3", "0, -4", "1, -1", "1, -2", "1, -3", "1, -4", + "2, -1", "2, -2", "2, -3", "2, -4", "3, -1", "3, -2", "3, -3", "3, -4", + ], + }, + { + path: "test/annexB/built-ins/String/prototype/substr/length-positive.js", + code: `return [ + "abc".substr(0, 1), "abc".substr(0, 2), "abc".substr(0, 3), "abc".substr(0, 4), + "abc".substr(1, 1), "abc".substr(1, 2), "abc".substr(1, 3), "abc".substr(1, 4), + "abc".substr(2, 1), "abc".substr(2, 2), "abc".substr(2, 3), "abc".substr(2, 4), + "abc".substr(3, 1), "abc".substr(3, 2), "abc".substr(3, 3), "abc".substr(3, 4), + ]`, + expected: ["a", "ab", "abc", "abc", "b", "bc", "bc", "bc", "c", "c", "c", "c", "", "", "", ""], + labels: [ + "0, 1", "0, 1", "0, 1", "0, 1", "1, 1", "1, 1", "1, 1", "1, 1", + "2, 1", "2, 1", "2, 1", "2, 1", "3, 1", "3, 1", "3, 1", "3, 1", + ], + }, + { + path: "test/annexB/built-ins/String/prototype/substr/length-falsey.js", + code: `return ["abc".substr(0, NaN), "abc".substr(1, NaN), "abc".substr(2, NaN), "abc".substr(3, NaN)]`, + expected: ["", "", "", ""], + labels: ["start: 0, length: NaN", "start: 1, length: NaN", "start: 2, length: NaN", "start: 3, length: NaN"], + }, + { + path: "test/annexB/built-ins/String/prototype/substr/length-undef.js", + code: `return [ + "abc".substr(0), "abc".substr(1), "abc".substr(2), "abc".substr(3), + "abc".substr(0, undefined), "abc".substr(1, undefined), "abc".substr(2, undefined), "abc".substr(3, undefined), + ]`, + expected: ["abc", "bc", "c", "", "abc", "bc", "c", ""], + labels: [ + "start: 0, length: unspecified", "start: 1, length: unspecified", "start: 2, length: unspecified", "start: 3, length: unspecified", + "start: 0, length: undefined", "start: 1, length: undefined", "start: 2, length: undefined", "start: 3, length: undefined", + ], + }, + { + path: "test/annexB/built-ins/String/prototype/substr/surrogate-pairs.js", + code: `return [ + "\uD834\uDF06".substr(0), "\uD834\uDF06".substr(1), "\uD834\uDF06".substr(2), + "\uD834\uDF06".substr(0, 0), "\uD834\uDF06".substr(0, 1), "\uD834\uDF06".substr(0, 2), + ]`, + expected: ["\uD834\uDF06", "\uDF06", "", "", "\uD834", "\uD834\uDF06"], + labels: ["start: 0", "start: 1", "start: 2", "end: 0", "end: 1", "end: 2"], + }, + { + path: "test/built-ins/String/prototype/includes/String.prototype.includes_FailMissingLetter.js", + code: `return ["word".includes("a", 0)]`, expected: [false], labels: ['"word".includes("a", 0)'], + }, + { + path: "test/built-ins/String/prototype/includes/String.prototype.includes_SuccessNoLocation.js", + code: `return ["word".includes("w")]`, expected: [true], labels: ['"word".includes("w")'], + }, + { + path: "test/built-ins/String/prototype/includes/String.prototype.includes_FailBadLocation.js", + code: `return ["word".includes("w", 5)]`, expected: [false], labels: ['"word".includes("w", 5)'], + }, + { + path: "test/built-ins/String/prototype/includes/String.prototype.includes_FailLocation.js", + code: `return ["word".includes("o", 3)]`, expected: [false], labels: ['"word".includes("o", 3)'], + }, + { + path: "test/built-ins/String/prototype/includes/String.prototype.includes_Success.js", + code: `return ["word".includes("w", 0)]`, expected: [true], labels: ['"word".includes("w", 0)'], + }, + { + path: "test/built-ins/String/prototype/includes/searchstring-found-with-position.js", + code: `const text = "The future is cool!"; return [text.includes("The future", 0), text.includes(" is ", 1), text.includes("cool!", 10)]`, + expected: [true, true, true], + labels: [ + 'Returns true for str.includes("The future", 0)', + 'Returns true for str.includes(" is ", 1)', + 'Returns true for str.includes("cool!", 10)', + ], + }, + { + path: "test/built-ins/String/prototype/includes/searchstring-found-without-position.js", + code: `const text = "The future is cool!"; return [text.includes("The future"), text.includes("is cool!"), text.includes(text)]`, + expected: [true, true, true], + labels: [ + 'Returns true for str.includes("The future")', + 'Returns true for str.includes("is cool!")', + "Returns true for str.includes(str)", + ], + }, + { + path: "test/built-ins/String/prototype/includes/searchstring-not-found-with-position.js", + code: `const text = "The future is cool!"; return [text.includes("The future", 1), text.includes(text, 1)]`, + expected: [false, false], + labels: ['Returns false on str.includes("The future", 1)', "Returns false on str.includes(str, 1)"], + }, + { + path: "test/built-ins/String/prototype/includes/searchstring-not-found-without-position.js", + code: `const text = "The future is cool!"; return [text.includes("Flash"), text.includes("FUTURE")]`, + expected: [false, false], labels: ["Flash if not included", "includes is case sensitive"], + }, + { + path: "test/built-ins/String/prototype/includes/return-false-with-out-of-bounds-position.js", + code: `const text = "The future is cool!"; return [ + text.includes("!", text.length + 1), text.includes("!", 100), text.includes("!", Infinity), text.includes("!", text.length), + ]`, + expected: [false, false, false, false], + labels: [ + 'str.includes("!", str.length + 1) returns false', 'str.includes("!", 100) returns false', + 'str.includes("!", Infinity) returns false', 'str.includes("!", str.length) returns false', + ], + }, + { + path: "test/built-ins/String/prototype/includes/return-true-if-searchstring-is-empty.js", + code: `const text = "The future is cool!"; return [text.includes("", text.length), text.includes(""), text.includes("", Infinity)]`, + expected: [true, true, true], + labels: ['str.includes("", str.length) returns true', 'str.includes("") returns true', 'str.includes("", Infinity) returns true'], + }, + { + path: "test/built-ins/String/prototype/includes/coerced-values-of-position.js", + code: `const text = "The future is cool!"; return [ + text.includes("The future", NaN), text.includes("The future", undefined), text.includes("The future", 0.4), + text.includes("The future", -1), text.includes("The future", 1.4), + ]`, + expected: [true, true, true, true, false], + labels: ["NaN coerced to 0", "undefined coerced to 0", "0.4 coerced to 0", "negative position", "1.4 coerced to 1"], + }, + { + path: "test/built-ins/String/prototype/startsWith/searchstring-found-with-position.js", + code: `const text = "The future is cool!"; return [text.startsWith("The future", 0), text.startsWith("future", 4), text.startsWith(" is cool!", 10)]`, + expected: [true, true, true], + labels: [ + 'str.startsWith("The future", 0) === true', 'str.startsWith("future", 4) === true', + 'str.startsWith(" is cool!", 10) === true', + ], + }, + { + path: "test/built-ins/String/prototype/startsWith/searchstring-found-without-position.js", + code: `const text = "The future is cool!"; return [text.startsWith("The "), text.startsWith("The future"), text.startsWith(text)]`, + expected: [true, true, true], + labels: ['str.startsWith("The ") === true', 'str.startsWith("The future") === true', "str.startsWith(str) === true"], + }, + { + path: "test/built-ins/String/prototype/startsWith/searchstring-not-found-with-position.js", + code: `const text = "The future is cool!"; return [text.startsWith("The future", 1), text.startsWith(text, 1)]`, + expected: [false, false], + labels: ['str.startsWith("The future", 1) === false', "str.startsWith(str, 1) === false"], + }, + { + path: "test/built-ins/String/prototype/startsWith/searchstring-not-found-without-position.js", + code: `const text = "The future is cool!"; return [text.startsWith("Flash"), text.startsWith("THE FUTURE"), text.startsWith("future is cool!")]`, + expected: [false, false, false], + labels: ['str.startsWith("Flash") === false', "startsWith is case sensitive", 'str.startsWith("future is cool!") === false'], + }, + { + path: "test/built-ins/String/prototype/startsWith/out-of-bounds-position.js", + code: `const text = "The future is cool!"; return [ + text.startsWith("!", text.length), text.startsWith("!", 100), text.startsWith("!", Infinity), + text.startsWith("The future", -1), text.startsWith("The future", -Infinity), + ]`, + expected: [false, false, false, true, true], + labels: [ + 'str.startsWith("!", str.length) returns false', 'str.startsWith("!", 100) returns false', + 'str.startsWith("!", Infinity) returns false', "position argument < 0 will search from the start of the string (-1)", + "position argument < 0 will search from the start of the string (-Infinity)", + ], + }, + { + path: "test/built-ins/String/prototype/startsWith/return-true-if-searchstring-is-empty.js", + code: `const text = "The future is cool!"; return [text.startsWith(""), text.startsWith("", text.length), text.startsWith("", Infinity)]`, + expected: [true, true, true], + labels: ['str.startsWith("") returns true', 'str.startsWith("", str.length) returns true', 'str.startsWith("", Infinity) returns true'], + }, + { + path: "test/built-ins/String/prototype/startsWith/coerced-values-of-position.js", + code: `const text = "The future is cool!"; return [ + text.startsWith("The future", NaN), text.startsWith("The future", undefined), + text.startsWith("The future", 0.4), text.startsWith("The future", 1.4), + ]`, + expected: [true, true, true, false], + labels: ["NaN coerced to 0", "undefined coerced to 0", "0.4 coerced to 0", "1.4 coerced to 1"], + }, + { + path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success.js", + code: `return ["word".endsWith("d")]`, expected: [true], labels: ['"word".endsWith("d")'], + }, + { + path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_2.js", + code: `return ["word".endsWith("d", 4)]`, expected: [true], labels: ['"word".endsWith("d", 4)'], + }, + { + path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_3.js", + code: `return ["word".endsWith("d", 25)]`, expected: [true], labels: ['"word".endsWith("d", 25)'], + }, + { + path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Success_4.js", + code: `return ["word".endsWith("r", 3)]`, expected: [true], labels: ['"word".endsWith("r", 3)'], + }, + { + path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail.js", + code: `return ["word".endsWith("r")]`, expected: [false], labels: ['"word".endsWith("r")'], + }, + { + path: "test/built-ins/String/prototype/endsWith/String.prototype.endsWith_Fail_2.js", + code: `return ["word".endsWith("d", 3)]`, expected: [false], labels: ['"word".endsWith("d", 3)'], + }, + { + path: "test/built-ins/String/prototype/endsWith/searchstring-found-with-position.js", + code: `const text = "The future is cool!"; return [text.endsWith("The future", 10), text.endsWith("future", 10), text.endsWith(" is cool!", text.length)]`, + expected: [true, true, true], + labels: [ + 'str.endsWith("The future", 10) === true', 'str.endsWith("future", 10) === true', + 'str.endsWith(" is cool!", str.length) === true', + ], + }, + { + path: "test/built-ins/String/prototype/endsWith/searchstring-found-without-position.js", + code: `const text = "The future is cool!"; return [text.endsWith("cool!"), text.endsWith("!"), text.endsWith(text)]`, + expected: [true, true, true], + labels: ['str.endsWith("cool!") === true', 'str.endsWith("!") === true', "str.endsWith(str) === true"], + }, + { + path: "test/built-ins/String/prototype/endsWith/searchstring-not-found-with-position.js", + code: `const text = "The future is cool!"; return [text.endsWith("is cool!", text.length - 1), text.endsWith("!", 1)]`, + expected: [false, false], + labels: ['str.endsWith("is cool!", str.length - 1) === false', 'str.endsWith("!", 1) === false'], + }, + { + path: "test/built-ins/String/prototype/endsWith/searchstring-not-found-without-position.js", + code: `const text = "The future is cool!"; return [text.endsWith("is Flash!"), text.endsWith("IS COOL!"), text.endsWith("The future")]`, + expected: [false, false, false], + labels: ['str.endsWith("is Flash!") === false', "endsWith is case sensitive", 'str.endsWith("The future") === false'], + }, + { + path: "test/built-ins/String/prototype/endsWith/return-false-if-search-start-is-less-than-zero.js", + code: `return ["web".endsWith("w", 0), "Bob".endsWith(" Bob")]`, + expected: [false, false], + labels: ['"web".endsWith("w", 0) returns false', '"Bob".endsWith(" Bob") returns false'], + }, + { + path: "test/built-ins/String/prototype/endsWith/return-true-if-searchstring-is-empty.js", + code: `const text = "The future is cool!"; return [ + text.endsWith(""), text.endsWith("", text.length), text.endsWith("", Infinity), + text.endsWith("", -1), text.endsWith("", -Infinity), + ]`, + expected: [true, true, true, true, true], + labels: [ + 'str.endsWith("") returns true', 'str.endsWith("", str.length) returns true', 'str.endsWith("", Infinity) returns true', + 'str.endsWith("", -1) returns true', 'str.endsWith("", -Infinity) returns true', + ], + }, + { + path: "test/built-ins/String/prototype/endsWith/coerced-values-of-position.js", + code: `const text = "The future is cool!"; return [ + text.endsWith("", NaN), text.endsWith("", undefined), text.endsWith("The future", 10.4), + ]`, + expected: [true, true, true], + labels: ["NaN coerced to 0", "undefined coerced to 0", "10.4 coerced to 10"], + }, + { + path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T1.js", + code: `return ["abcd".indexOf("abcdab")]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab")===-1'], + }, + { + path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T2.js", + code: `return ["abcd".indexOf("abcdab", 0)]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab",0)===-1'], + }, + { + path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T3.js", + code: `return ["abcd".indexOf("abcdab", 99)]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab",99)===-1'], + }, + { + path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A2_T4.js", + code: `return ["abcd".indexOf("abcdab", NaN)]`, expected: [-1], labels: ['#1: "abcd".indexOf("abcdab",NaN)===-1'], + }, + { + path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T1.js", + code: `return ["$$abcdabcd".indexOf("ab", NaN)]`, expected: [2], labels: ['#1: "$$abcdabcd".indexOf("ab",NaN)===2'], + }, + { + path: "test/built-ins/String/prototype/indexOf/S15.5.4.7_A3_T3.js", + code: `return ["$$abcdabcd".indexOf("ab", -Infinity)]`, expected: [2], labels: ['#1: "$$abcdabcd".indexOf("ab", function(){return -Infinity;}())===2'], + }, + { + path: "test/built-ins/String/prototype/indexOf/position-tointeger.js", + code: `return [ + "aaaa".indexOf("aa", 0), "aaaa".indexOf("aa", 1), "aaaa".indexOf("aa", -0.9), + "aaaa".indexOf("aa", 0.9), "aaaa".indexOf("aa", 1.9), "aaaa".indexOf("aa", NaN), + "aaaa".indexOf("aa", Infinity), "aaaa".indexOf("aa", undefined), + "aaaa".indexOf("aa", 2), "aaaa".indexOf("aa", 2.9), + ]`, + expected: [0, 1, 0, 0, 1, 0, -1, 0, 2, 2], + labels: [ + "position 0", "position 1", "ToInteger: truncate towards 0 (-0.9)", "ToInteger: truncate towards 0 (0.9)", + "ToInteger: truncate towards 0 (1.9)", "ToInteger: NaN => 0", "position Infinity", + "ToInteger: undefined => NaN => 0", "position 2", "ToInteger: truncate towards 0 (2.9)", + ], + }, + { + path: "test/built-ins/String/prototype/indexOf/searchstring-tostring.js", + code: `return ["foo".indexOf(""), "__foo__".indexOf("foo")]`, + expected: [0, 2], labels: ['"foo".indexOf("")', '"__foo__".indexOf("foo")'], + }, + { + path: "test/built-ins/String/prototype/lastIndexOf/not-a-substring.js", + code: `return ["abc".lastIndexOf("d")]`, + expected: [-1], + labels: ["String.prototype.lastIndexOf returns -1 when searchString is shorter than this and searchString is not a substring of this."], + }, +] as const + +describe("Test262-adapted String search and extraction behavior", () => { + for (const item of cases) { + test(item.path, async () => { + const actual = await value(item.code) + if (!Array.isArray(actual)) throw new Error(`expected assertion values for ${item.path}`) + expect(actual.length, "adapted assertion count").toBe(item.expected.length) + item.expected.forEach((expected, index) => expect(actual[index], item.labels[index]!).toEqual(expected)) + }) + } +}) diff --git a/packages/codemode/test/test262-string.md b/packages/codemode/test/test262-string.md new file mode 100644 index 00000000000..94d01f83d9d --- /dev/null +++ b/packages/codemode/test/test262-string.md @@ -0,0 +1,69 @@ +# Test262 String Coverage + +The String tests adapt Test262 at revision `250f204f23a9249ff204be2baec29600faae7b75`. They cover CodeMode's 32 +exposed instance methods and two static methods using primitive receivers, accepted argument types, and deterministic +behavior. Each executable case names its exact upstream source path. `LICENSE.test262` contains the upstream BSD terms. + +This is coverage of CodeMode's bounded String surface, not a claim of ECMAScript or Test262 conformance. One upstream +file may contain both adapted and inapplicable assertions, so a cited source means only that the represented assertions +were adapted. + +## Inventory + +The relevant upstream directories contain 1,048 files: 1,009 core built-in files, 29 Annex B files for exposed methods, +and 10 Intl `localeCompare` files. The executable suite adapts assertions from 298 distinct sources. + +| API | Upstream files | Adapted sources | +| --- | ---: | ---: | +| `String.fromCharCode` | 17 | 6 | +| `String.fromCodePoint` | 11 | 4 | +| `String.prototype.at` | 11 | 5 | +| `String.prototype.charAt` | 30 | 9 | +| `String.prototype.charCodeAt` | 25 | 4 | +| `String.prototype.codePointAt` | 16 | 6 | +| `String.prototype.concat` | 22 | 1 | +| `String.prototype.endsWith` | 27 | 13 | +| `String.prototype.includes` | 27 | 12 | +| `String.prototype.indexOf` | 47 | 8 | +| `String.prototype.lastIndexOf` | 25 | 1 | +| `String.prototype.localeCompare` | 23 | 1 | +| `String.prototype.match` | 52 | 9 | +| `String.prototype.matchAll` | 26 | 1 | +| `String.prototype.normalize` | 14 | 3 | +| `String.prototype.padEnd` | 13 | 4 | +| `String.prototype.padStart` | 13 | 4 | +| `String.prototype.repeat` | 16 | 4 | +| `String.prototype.replace` | 56 | 16 | +| `String.prototype.replaceAll` | 46 | 12 | +| `String.prototype.search` | 44 | 10 | +| `String.prototype.slice` | 38 | 11 | +| `String.prototype.split` | 121 | 50 | +| `String.prototype.startsWith` | 21 | 7 | +| `String.prototype.substr` | 15 | 6 | +| `String.prototype.substring` | 46 | 12 | +| `String.prototype.toLowerCase` | 30 | 5 | +| `String.prototype.toString` | 7 | 1 | +| `String.prototype.toUpperCase` | 26 | 3 | +| `String.prototype.trim` | 129 | 66 | +| `String.prototype.trimEnd` | 23 | 2 | +| `String.prototype.trimLeft` | 4 | 0 | +| `String.prototype.trimRight` | 4 | 0 | +| `String.prototype.trimStart` | 23 | 2 | + +## Exclusions + +Assertions are not adapted when they test behavior outside CodeMode's documented String surface: + +- Function metadata, property descriptors, constructibility, prototype mutation, or cross-realm identity. +- The `trimLeft`/`trimRight` Test262 files assert prototype function identity, which CodeMode does not expose. Their + supported call behavior remains covered by CodeMode-specific tests. +- Boxed strings, generic receivers, custom coercion objects, Symbols, BigInts, or argument types CodeMode rejects. +- Symbol-based RegExp dispatch, custom matchers, species constructors, or iterator protocol details. CodeMode materializes + `matchAll` results instead of exposing iterators. +- Locale selection and options. CodeMode deliberately uses the host default locale and ignores those arguments. +- Test262 harness behavior or setup syntax unavailable in the confined interpreter. +- Function-replacer behavior that is covered by CodeMode-specific tests for sequential callbacks, async tool calls, + result coercion, diagnostics, and sandbox boundaries. +- Assertions requiring an exact native error type when CodeMode deliberately exposes only its safe runtime error. + +Handwritten tests remain where they specify CodeMode behavior rather than ordinary ECMAScript String semantics.