Merge remote-tracking branch 'origin/v2' into promise-chaining

# Conflicts:
#	packages/codemode/src/interpreter/runtime.ts
#	packages/codemode/src/tool-runtime.ts
This commit is contained in:
Aiden Cline 2026-07-08 17:33:50 -05:00
commit 6273304f2a
481 changed files with 23197 additions and 13412 deletions

View file

@ -62,7 +62,7 @@ const result =
`result` is always a `CodeMode.Result`. Program, validation, limit, and tool failures are returned as diagnostics rather than failing the Effect. Host interruption remains interruption.
Successful result values are JSON-safe data. A program that returns `undefined`, including by reaching the end without `return`, produces `null`; nested `undefined` values are normalized to `null` as well.
Successful result values are JSON-safe data. An explicit `return` produces the program result; when it is omitted, the final executable top-level expression is returned as a model-friendly REPL convenience. Otherwise reaching the end produces `null`. Returned `undefined` and nested `undefined` values are normalized to `null` as well.
## API
@ -241,7 +241,7 @@ CodeMode executes a deliberately bounded JavaScript subset. It supports:
- `if`, conditional expressions, `switch`, `for`, `for...of` (arrays, strings, Maps, Sets, including assignment-form destructuring such as `for ([key, value] of entries)`), `for...in` (own keys of plain objects, index strings of arrays, and namespace/tool names of `tools` references - anything else is an error suggesting `for...of` or `Object.keys`, rather than real JS's surprising behavior of indices for strings and zero iterations for Maps/Sets), `while`, and `do...while`.
- Arrow functions and function declarations with closures, defaults, rest parameters, and destructuring.
- Optional chaining, nullish coalescing, templates, spread (arrays, strings, Maps, Sets), and `try`/`catch`.
- Common array, string, number, `Object`, `Math`, and `JSON` operations. Mutating array methods include `push`/`pop`/`shift`/`unshift`/`splice` (removes in place and returns the removed elements)/`fill`/`copyWithin`; array `keys`/`values`/`entries` return **arrays** (matching the Map/Set convention) and work with `for...of` and spread. String methods include `localeCompare` (locale/options arguments ignored), `normalize`, and the `trimLeft`/`trimRight` aliases. `Object.keys` also accepts arrays (index strings, as in JS) and tool references: `Object.keys(tools)` lists the top-level namespaces, including `$codemode`, and `Object.keys(tools.ns)` lists the names at that node (a callable tool enumerates as `[]`; an unknown path is an `UnknownTool` diagnostic). `Object.values`/`Object.entries` on a tool reference fail with a pointer at `Object.keys(tools)` and `tools.$codemode.search`.
- Common array, string, number, `Object`, `Math`, and `JSON` operations, including primitive-number `valueOf`, the standard non-finite `Number` constants, and host-backed `Math.random`. Mutating array methods include `push`/`pop`/`shift`/`unshift`/`splice` (removes in place and returns the removed elements)/`fill`/`copyWithin`; array `keys`/`values`/`entries` return **arrays** (matching the Map/Set convention) and work with `for...of` and spread. String methods include `localeCompare` (locale/options arguments ignored), `normalize`, and the `trimLeft`/`trimRight` aliases. `Object.keys` also accepts arrays (index strings, as in JS) and tool references: `Object.keys(tools)` lists the top-level namespaces, including `$codemode`, and `Object.keys(tools.ns)` lists the names at that node (a callable tool enumerates as `[]`; an unknown path is an `UnknownTool` diagnostic). `Object.values`/`Object.entries` on a tool reference fail with a pointer at `Object.keys(tools)` and `tools.$codemode.search`.
- `Date` - `Date.now()`/`Date.parse()`/`Date.UTC()`, `new Date(...)`, the getter methods, and date arithmetic/comparison via the time value. Dates stringify as ISO (`toString` included, for determinism across host timezones).
- Regular expressions - `/literals/` and `new RegExp(...)` with `test`/`exec` (stateful `lastIndex` for `g`), plus string `match`/`matchAll`/`replace`/`replaceAll`/`split`/`search` with patterns. Match results are arrays carrying `index` and named `groups` as own properties (`input` is omitted). `replace` and `replaceAll` accept function replacers with captures, offset, input, and named groups; callbacks run sequentially, may await tool calls, and have their results coerced to strings. Invalid patterns, invalid flags, and missing-`g` calls fail with catchable errors that say what was wrong and how to fix it (escaping hints, the exact `/pattern/g` to write). Patterns run on the host engine, so pathological backtracking is bounded only by the execution timeout.
- `Map` and `Set` - construction from entries/arrays/strings, `get`/`set`/`add`/`has`/`delete`/`clear`/`size`/`forEach`, and `keys`/`values`/`entries` returning **arrays** (not iterators).

View file

@ -207,7 +207,7 @@ current omissions to implement, not intentional product boundaries.
- [ ] Add `Object.is` after runtime method and tool references have stable identity semantics.
- [ ] Add deterministic modern collection conveniences where they improve orchestration: `Object.groupBy`, Set
composition methods, and `Array.prototype.toSpliced`.
- [ ] Decide whether nondeterministic `Math.random` and iterable `Math.sumPrecise` belong in the runtime.
- [ ] Decide whether iterable `Math.sumPrecise` belongs in the runtime.
- [ ] Refine diagnostics so user throws, expected tool failures, unexpected host/tool defects, and genuine interpreter
defects are distinguishable without leaking private causes.

View file

@ -30,7 +30,6 @@ export type Binding = {
export type StatementResult =
| { kind: "none" }
| { kind: "value"; value: unknown }
| { kind: "return"; value: unknown }
| { kind: "break" }
| { kind: "continue" }

View file

@ -535,17 +535,29 @@ const invokeArrayStatic = (name: string, args: Array<unknown>, node: AstNode): u
if (args[0] instanceof SandboxURLSearchParams) {
return Array.from(args[0].params.entries(), ([key, value]) => [key, value])
}
const source = boundedData(args[0], "Array.from input")
const source = args[0]
if (source instanceof SandboxPromise) {
throw new InterpreterRuntimeError(
"Array.from received an un-awaited Promise; await it before creating the array.",
node,
"InvalidDataValue",
)
}
if (typeof source === "string") return Array.from(source)
if (Array.isArray(source)) return [...source]
if (
source !== null &&
typeof source === "object" &&
(Object.getPrototypeOf(source) === Object.prototype || Object.getPrototypeOf(source) === null) &&
typeof (source as { length?: unknown }).length === "number"
) {
return Array.from(source as ArrayLike<unknown>)
}
throw new InterpreterRuntimeError("Array.from expects an array, string, Map, Set, or array-like value.", node)
throw new InterpreterRuntimeError(
"Array.from expects an array, string, Map, Set, or array-like value.",
node,
"InvalidDataValue",
)
}
default:
throw new InterpreterRuntimeError(`Array.${name} is not available in CodeMode.`, node)
@ -684,13 +696,15 @@ class Interpreter<R> {
const evaluate = Effect.gen(function* () {
self.hoistFunctions(program.body)
let value: unknown = undefined
let returned = false
for (const statement of program.body) {
for (const [index, statement] of program.body.entries()) {
if (index === program.body.length - 1 && statement.type === "ExpressionStatement") {
value = yield* self.evaluateExpression(getNode(statement, "expression"))
break
}
const result = yield* self.evaluateStatement(statement)
if (result.kind === "return") {
value = result.value
returned = true
break
}
@ -698,11 +712,7 @@ class Interpreter<R> {
throw new InterpreterRuntimeError(`Unexpected '${result.kind}' outside of a loop.`, statement)
}
if (result.kind === "value") {
self.lastValue = result.value
}
}
if (!returned) value = self.lastValue
// The program body runs inside an implicit async function, so a returned promise
// resolves before crossing the data boundary - `return tools.ns.tool(...)` works
@ -827,7 +837,7 @@ class Interpreter<R> {
private evaluateStatement(node: AstNode): Effect.Effect<StatementResult, unknown, R> {
switch (node.type) {
case "ExpressionStatement":
return Effect.map(this.evaluateExpression(getNode(node, "expression")), (value) => ({ kind: "value", value }))
return Effect.as(this.evaluateExpression(getNode(node, "expression")), { kind: "none" })
case "VariableDeclaration":
return Effect.map(this.evaluateVariableDeclaration(node), () => ({ kind: "none" }))
case "ReturnStatement": {
@ -880,11 +890,6 @@ class Interpreter<R> {
const statement = asNode(statementValue, "body")
const result = yield* self.evaluateStatement(statement)
if (result.kind === "value") {
self.lastValue = result.value
continue
}
if (result.kind !== "none") {
return result
}
@ -976,7 +981,6 @@ class Interpreter<R> {
const result = yield* self.evaluateStatement(asNode(statementValue, "consequent"))
if (result.kind === "break") return { kind: "none" } satisfies StatementResult
if (result.kind === "return" || result.kind === "continue") return result
if (result.kind === "value") self.lastValue = result.value
}
}
return { kind: "none" } satisfies StatementResult
@ -1004,9 +1008,6 @@ class Interpreter<R> {
return result
}
if (result.kind === "value") {
self.lastValue = result.value
}
}
return { kind: "none" } satisfies StatementResult
@ -1034,9 +1035,6 @@ class Interpreter<R> {
return result
}
if (result.kind === "value") {
self.lastValue = result.value
}
} while (yield* self.evaluateExpression(testNode))
return { kind: "none" } satisfies StatementResult
@ -1092,10 +1090,6 @@ class Interpreter<R> {
return { kind: "none" } satisfies StatementResult
}
if (result.kind === "value") {
self.lastValue = result.value
}
if (iterationScope) {
const loopScope = self.currentScope()
for (const name of perIterationBindings) {
@ -1180,10 +1174,6 @@ class Interpreter<R> {
return { kind: "none" }
}
if (result.kind === "value") {
self.lastValue = result.value
}
if (result.kind === "continue") {
continue
}
@ -1273,10 +1263,6 @@ class Interpreter<R> {
return { kind: "none" }
}
if (result.kind === "value") {
self.lastValue = result.value
}
if (result.kind === "continue") {
continue
}
@ -2077,6 +2063,9 @@ class Interpreter<R> {
if (callable.namespace === "Object" && objectMethodsPreservingIdentity.has(callable.name)) {
return invokeGlobalMethod(callable, args, node)
}
if (callable.namespace === "Array" && (callable.name === "from" || callable.name === "of")) {
return invokeGlobalMethod(callable, args, node)
}
return boundedData(invokeGlobalMethod(callable, args, node), `${callable.namespace}.${callable.name} result`)
}
if (callable instanceof CoercionFunction) {
@ -2409,7 +2398,7 @@ class Interpreter<R> {
if (fn.body.type === "BlockStatement") {
const result = yield* invocation.evaluateStatement(fn.body)
return result.kind === "return" || result.kind === "value" ? result.value : undefined
return result.kind === "return" ? result.value : undefined
}
return yield* invocation.evaluateExpression(fn.body)

View file

@ -1,6 +1,7 @@
export const mathConstants = new Set(["PI", "E", "LN2", "LN10", "LOG2E", "LOG10E", "SQRT2", "SQRT1_2"])
export const mathMethods = new Set([
"random",
"max",
"min",
"abs",
@ -40,6 +41,7 @@ export const mathMethods = new Set([
export const invokeMathMethod = (name: string, args: Array<unknown>, node: AstNode): number => {
if (!mathMethods.has(name)) throw new InterpreterRuntimeError(`Math.${name} is not available in CodeMode.`, node)
if (name === "random") return Math.random()
const nums = args.map((arg) => {
if (typeof arg !== "number") throw new InterpreterRuntimeError(`Math.${name} expects number arguments.`, node)
return arg

View file

@ -1,6 +1,15 @@
export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString"])
export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString", "valueOf"])
export const numberConstants = new Set(["MAX_SAFE_INTEGER", "MIN_SAFE_INTEGER", "MAX_VALUE", "MIN_VALUE", "EPSILON"])
export const numberConstants = new Set([
"MAX_SAFE_INTEGER",
"MIN_SAFE_INTEGER",
"MAX_VALUE",
"MIN_VALUE",
"EPSILON",
"NaN",
"POSITIVE_INFINITY",
"NEGATIVE_INFINITY",
])
export const numberStatics = new Set(["isInteger", "isFinite", "isNaN", "isSafeInteger", "parseInt", "parseFloat"])
@ -32,6 +41,9 @@ export const invokeNumberMethod = (value: number, name: string, args: Array<unkn
result = value.toString(radix)
break
}
case "valueOf":
result = value
break
default:
throw new InterpreterRuntimeError(`Number method '${name}' is not available in CodeMode.`, node)
}

View file

@ -1,6 +1,6 @@
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
import { isBlockedMember } from "../tool-runtime.js"
import { isSandboxValue, SandboxMap, SandboxSet, SandboxURLSearchParams } from "../values.js"
import { isSandboxValue, SandboxMap, SandboxPromise, SandboxSet, SandboxURLSearchParams } from "../values.js"
import { boundedData, coerceToString } from "./value.js"
export const objectStatics = new Set(["keys", "values", "entries", "hasOwn", "assign", "fromEntries"])
@ -10,13 +10,23 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
if (!objectStatics.has(name)) throw new InterpreterRuntimeError(`Object.${name} is not available in CodeMode.`, node)
const requireObject = (): Record<string, unknown> => {
const input = args[0]
const value = boundedData(args[0], `Object.${name} input`)
if (Array.isArray(input)) return input as unknown as Record<string, unknown>
if (isSandboxValue(value)) return {}
if (value === null || typeof value !== "object") {
throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node)
if (isSandboxValue(input)) return {}
if (input instanceof SandboxPromise) {
throw new InterpreterRuntimeError(
`Object.${name} received an un-awaited Promise; await it before inspecting the result.`,
node,
"InvalidDataValue",
)
}
return value as Record<string, unknown>
if (input === null || typeof input !== "object") {
throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue")
}
const prototype = Object.getPrototypeOf(input)
if (prototype !== null && prototype !== Object.prototype) {
throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue")
}
return input as Record<string, unknown>
}
const guardedSet = (out: Record<string, unknown>, key: string, item: unknown): void => {
if (isBlockedMember(key)) throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, node)
@ -28,15 +38,8 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
guardedSet(out, coerceToString(key), item)
}
switch (name) {
case "keys": {
const value = boundedData(args[0], "Object.keys input")
if (isSandboxValue(value)) return []
if (Array.isArray(value)) return Object.keys(value)
if (value === null || typeof value !== "object") {
throw new InterpreterRuntimeError("Object.keys expects a data object or array.", node)
}
return Object.keys(value)
}
case "keys":
return Object.keys(requireObject())
case "values":
return Object.values(requireObject())
case "entries":

View file

@ -617,6 +617,7 @@ export const prepare = <R>(tools: HostTools<R>, catalogBudget = defaultCatalogBu
"",
"Use common JavaScript data operations, functions, control flow, selected standard-library methods, and awaited tool calls. Built-ins include Date, RegExp, Map, Set, URL, URLSearchParams, and URI encoding helpers.",
"Modules/imports, classes, generators, timers, fetch, eval, prototype access, and unlisted methods are unavailable. Use Code Mode tools for external operations. Use await with try/catch.",
"Prefer explicit `return`; otherwise only the final top-level expression becomes the result.",
"Dates and URLs serialize to strings at data boundaries; Map/Set/RegExp/URLSearchParams serialize to `{}`.",
]

View file

@ -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

View file

@ -0,0 +1,325 @@
/*
* Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
* - test/built-ins/Array/prototype/map/15.4.4.19-8-1.js
* - test/built-ins/Array/prototype/map/15.4.4.19-8-2.js
* - test/built-ins/Array/prototype/map/15.4.4.19-8-b-1.js
* - test/built-ins/Array/prototype/filter/15.4.4.20-9-1.js
* - test/built-ins/Array/prototype/filter/15.4.4.20-9-2.js
* - test/built-ins/Array/prototype/filter/15.4.4.20-9-b-1.js
* - test/built-ins/Array/prototype/find/predicate-call-parameters.js
* - test/built-ins/Array/prototype/find/predicate-not-called-on-empty-array.js
* - test/built-ins/Array/prototype/find/return-found-value-predicate-result-is-true.js
* - test/built-ins/Array/prototype/find/return-undefined-if-predicate-returns-false-value.js
* - test/built-ins/Array/prototype/findIndex/predicate-call-parameters.js
* - test/built-ins/Array/prototype/findIndex/return-index-predicate-result-is-true.js
* - test/built-ins/Array/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js
* - test/built-ins/Array/prototype/findLast/predicate-call-parameters.js
* - test/built-ins/Array/prototype/findLast/return-found-value-predicate-result-is-true.js
* - test/built-ins/Array/prototype/findLast/return-undefined-if-predicate-returns-false-value.js
* - test/built-ins/Array/prototype/findLastIndex/predicate-call-parameters.js
* - test/built-ins/Array/prototype/findLastIndex/return-index-predicate-result-is-true.js
* - test/built-ins/Array/prototype/findLastIndex/return-negative-one-if-predicate-returns-false-value.js
* - test/built-ins/Array/prototype/some/15.4.4.17-7-1.js
* - test/built-ins/Array/prototype/some/15.4.4.17-8-1.js
* - test/built-ins/Array/prototype/every/15.4.4.16-7-1.js
* - test/built-ins/Array/prototype/every/15.4.4.16-8-1.js
* - test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js
* - test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js
* - test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js
* - test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js
* - test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js
* - test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js
* - test/built-ins/Array/prototype/flatMap/depth-always-one.js
* - test/built-ins/Array/prototype/flatMap/mapperfunction-throws.js
* - test/built-ins/Array/prototype/sort/S15.4.4.11_A1.1_T1.js
* - test/built-ins/Array/prototype/sort/S15.4.4.11_A2.1_T1.js
* - test/built-ins/Array/prototype/sort/stability-5-elements.js
* - test/built-ins/Array/prototype/toSorted/comparefn-controls-sort.js
* - test/built-ins/Array/prototype/toSorted/comparefn-default.js
* - test/built-ins/Array/prototype/toSorted/immutable.js
* - test/built-ins/Array/prototype/toSorted/zero-or-one-element.js
*
* Copyright (C) 2015 the V8 project authors. All rights reserved.
* Copyright (C) 2018 Mathias Bynens. All rights reserved.
* Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
* Copyright (C) 2021 Igalia, S.L. All rights reserved.
* Copyright (C) 2021 Microsoft. All rights reserved.
* Copyright (C) 2025 Google. All rights reserved.
* Copyright (C) 2026 Garham Lee. All rights reserved.
* Copyright (c) 2012 Ecma International. All rights reserved.
* Copyright 2009 the Sputnik authors. 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/Array/prototype/map/15.4.4.19-8-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const result = input.map((value) => { input[2] = 3; input[5] = 6; return 1 }); return [result.length, result[5] === undefined]`,
expected: [5, true],
},
{
path: "test/built-ins/Array/prototype/map/15.4.4.19-8-2.js",
code: `const input = [1, 2, 3, 4, 5]; const result = input.map((value) => { input[4] = -1; return value > 0 ? 1 : 0 }); return [result.length, result[4]]`,
expected: [5, 0],
},
{
path: "test/built-ins/Array/prototype/map/15.4.4.19-8-b-1.js",
code: `const input = []; input[10] = 0; input.pop(); input[1] = undefined; let calls = 0; const result = input.map(() => { calls += 1; return 1 }); return [result.length, calls, 0 in result, 1 in result]`,
expected: [10, 1, false, true],
},
{
path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const result = input.filter(() => { input[2] = 3; input[5] = 6; return true }); return result`,
expected: [1, 2, 3, 4, 5],
},
{
path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-2.js",
code: `const input = [1, 2, 3, 4, 5]; return input.filter((value) => { input[2] = -1; input[4] = -1; return value > 0 })`,
expected: [1, 2, 4],
},
{
path: "test/built-ins/Array/prototype/filter/15.4.4.20-9-b-1.js",
code: `const input = []; input[9] = 0; input.pop(); input[1] = undefined; let calls = 0; const result = input.filter(() => { calls += 1; return false }); return [result, calls]`,
expected: [[], 1],
},
{
path: "test/built-ins/Array/prototype/find/predicate-call-parameters.js",
code: `const input = [10, 20]; const seen = []; input.find((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
expected: [
[10, 0, true],
[20, 1, true],
],
},
{
path: "test/built-ins/Array/prototype/find/return-found-value-predicate-result-is-true.js",
code: `return [1, 2, 3].find((value) => value > 1)`,
expected: 2,
},
{
path: "test/built-ins/Array/prototype/find/return-undefined-if-predicate-returns-false-value.js",
code: `return [1, 2, 3].find((value) => value > 4) === undefined`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/find/predicate-not-called-on-empty-array.js",
code: `let calls = 0; const result = [].find(() => { calls += 1; return true }); return [result === undefined, calls]`,
expected: [true, 0],
},
{
path: "test/built-ins/Array/prototype/findIndex/predicate-call-parameters.js",
code: `const input = [10, 20]; const seen = []; input.findIndex((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
expected: [
[10, 0, true],
[20, 1, true],
],
},
{
path: "test/built-ins/Array/prototype/findIndex/return-index-predicate-result-is-true.js",
code: `return [1, 2, 3].findIndex((value) => value > 1)`,
expected: 1,
},
{
path: "test/built-ins/Array/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js",
code: `return [1, 2, 3].findIndex((value) => value > 4)`,
expected: -1,
},
{
path: "test/built-ins/Array/prototype/findLast/predicate-call-parameters.js",
code: `const input = [10, 20]; const seen = []; input.findLast((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
expected: [
[20, 1, true],
[10, 0, true],
],
},
{
path: "test/built-ins/Array/prototype/findLast/return-found-value-predicate-result-is-true.js",
code: `return [1, 2, 3].findLast((value) => value < 3)`,
expected: 2,
},
{
path: "test/built-ins/Array/prototype/findLast/return-undefined-if-predicate-returns-false-value.js",
code: `return [1, 2, 3].findLast((value) => value > 4) === undefined`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/findLastIndex/predicate-call-parameters.js",
code: `const input = [10, 20]; const seen = []; input.findLastIndex((value, index, receiver) => { seen.push([value, index, receiver === input]); return false }); return seen`,
expected: [
[20, 1, true],
[10, 0, true],
],
},
{
path: "test/built-ins/Array/prototype/findLastIndex/return-index-predicate-result-is-true.js",
code: `return [1, 2, 3].findLastIndex((value) => value < 3)`,
expected: 1,
},
{
path: "test/built-ins/Array/prototype/findLastIndex/return-negative-one-if-predicate-returns-false-value.js",
code: `return [1, 2, 3].findLastIndex((value) => value > 4)`,
expected: -1,
},
{
path: "test/built-ins/Array/prototype/some/15.4.4.17-7-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const seen = []; const result = input.some((value) => { input[2] = 3; seen.push(value); return false }); return [result, seen.includes(3)]`,
expected: [false, true],
},
{
path: "test/built-ins/Array/prototype/some/15.4.4.17-8-1.js",
code: `return [].some(() => true)`,
expected: false,
},
{
path: "test/built-ins/Array/prototype/every/15.4.4.16-7-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; const seen = []; const result = input.every((value) => { input[2] = 3; seen.push(value); return true }); return [result, seen.includes(3)]`,
expected: [true, true],
},
{
path: "test/built-ins/Array/prototype/every/15.4.4.16-8-1.js",
code: `return [].every(() => false)`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = 5; let calls = 0; input.forEach(() => { calls += 1; input[2] = 3; input[5] = 6 }); return calls`,
expected: 5,
},
{
path: "test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js",
code: `const input = [1, 2, 3]; const seen = []; input.forEach((value, index) => { seen.push(value); if (index === 0) input.pop() }); return seen`,
expected: [1, 2],
},
{
path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js",
code: `const input = [1, 2]; input[3] = 4; input[4] = "5"; return input.reduce((accumulator, value) => { input[2] = 3; input[5] = 6; return accumulator + value })`,
expected: "105",
},
{
path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js",
code: `let calls = 0; const result = [1].reduce(() => { calls += 1; return 2 }); return [result, calls]`,
expected: [1, 0],
},
{
path: "test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js",
code: `const input = [1, 2, 3, 4, 5]; input.reduce(() => 1); return input`,
expected: [1, 2, 3, 4, 5],
},
{
path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js",
code: `const input = ["1", 2]; input[3] = 4; input[4] = "5"; return input.reduceRight((accumulator, value) => { input[2] = 3; input[5] = 6; return accumulator + value })`,
expected: "54321",
},
{
path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js",
code: `let calls = 0; const result = [1].reduceRight(() => { calls += 1; return 2 }); return [result, calls]`,
expected: [1, 0],
},
{
path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js",
code: `const input = [1, 2, 3, 4, 5]; input.reduceRight(() => 1); return input`,
expected: [1, 2, 3, 4, 5],
},
{
path: "test/built-ins/Array/prototype/flatMap/depth-always-one.js",
code: `return [1, 2, 3].flatMap((value) => [[value * 2]])`,
expected: [[2], [4], [6]],
},
{
path: "test/built-ins/Array/prototype/flatMap/mapperfunction-throws.js",
code: `try { [1, 2].flatMap(() => { throw "stop" }) } catch (error) { return error === "stop" } return false`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/sort/S15.4.4.11_A1.1_T1.js",
code: `const input = []; input[2] = 0; input.pop(); input.sort(); return [input.length, input[0] === undefined, input[1] === undefined]`,
expected: [2, true, true],
},
{
path: "test/built-ins/Array/prototype/sort/S15.4.4.11_A2.1_T1.js",
code: `return ["z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n", "M", "L", "K", "J", "I", "H", "G", "F", "E", "D", "C", "B", "A"].sort()`,
expected: [
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
],
},
{
path: "test/built-ins/Array/prototype/sort/stability-5-elements.js",
code: `const input = [{ n: "A", r: 2 }, { n: "B", r: 3 }, { n: "C", r: 2 }, { n: "D", r: 3 }, { n: "E", r: 3 }]; return input.sort((left, right) => right.r - left.r).map((item) => item.n).join("")`,
expected: "BDEAC",
},
{
path: "test/built-ins/Array/prototype/toSorted/comparefn-controls-sort.js",
code: `const mixed = [333, 33, 3, 222, 22, 2, 111, 11, 1]; return [[1, 2, 3, 4].toSorted((a, b) => a - b), [4, 3, 2, 1].toSorted((a, b) => a - b), mixed.toSorted((a, b) => a - b), [1, 2, 3, 4].toSorted((a, b) => b - a), [4, 3, 2, 1].toSorted((a, b) => b - a), mixed.toSorted((a, b) => b - a)]`,
expected: [
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 11, 22, 33, 111, 222, 333],
[4, 3, 2, 1],
[4, 3, 2, 1],
[333, 222, 111, 33, 22, 11, 3, 2, 1],
],
},
{
path: "test/built-ins/Array/prototype/toSorted/comparefn-default.js",
code: `return [[1, 2, 3, 4].toSorted(), [4, 3, 2, 1].toSorted(), ["a", 2, 1, "z"].toSorted(), [333, 33, 3, 222, 22, 2, 111, 11, 1].toSorted()]`,
expected: [
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, "a", "z"],
[1, 11, 111, 2, 22, 222, 3, 33, 333],
],
},
{
path: "test/built-ins/Array/prototype/toSorted/immutable.js",
code: `const input = [2, 0, 1]; const result = input.toSorted(); return [input, result !== input]`,
expected: [[2, 0, 1], true],
},
{
path: "test/built-ins/Array/prototype/toSorted/zero-or-one-element.js",
code: `const zero = []; const one = [1]; const zeroResult = zero.toSorted(); const oneResult = one.toSorted(); return [zeroResult, oneResult, zeroResult !== zero, oneResult !== one]`,
expected: [[], [1], true, true],
},
] as const
describe("Test262 Array callback adaptations", () => {
for (const item of cases) {
test(item.path, async () => {
expect(await value(item.code)).toEqual(item.expected)
})
}
})

View file

@ -0,0 +1,323 @@
/*
* Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
* - test/built-ins/Array/prototype/includes/samevaluezero.js
* - test/built-ins/Array/prototype/includes/using-fromindex.js
* - test/built-ins/Array/prototype/join/S15.4.4.5_A3.1_T1.js
* - test/built-ins/Array/prototype/join/S15.4.4.5_A3.2_T1.js
* - test/built-ins/Array/prototype/slice/S15.4.4.10_A1.2_T2.js
* - test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T1.js
* - test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T2.js
* - test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T3.js
* - test/built-ins/Array/prototype/indexOf/fromindex-zero-conversion.js
* - test/built-ins/Array/prototype/indexOf/length-zero-returns-minus-one.js
* - test/built-ins/Array/prototype/lastIndexOf/fromindex-zero-conversion.js
* - test/built-ins/Array/prototype/lastIndexOf/length-zero-returns-minus-one.js
* - test/built-ins/Array/prototype/at/returns-item.js
* - test/built-ins/Array/prototype/at/returns-item-relative-index.js
* - test/built-ins/Array/prototype/at/returns-undefined-for-out-of-range-index.js
* - test/built-ins/Array/prototype/flat/null-undefined-elements.js
* - test/built-ins/Array/prototype/flat/positive-infinity.js
* - test/built-ins/Array/prototype/reverse/S15.4.4.8_A1_T1.js
* - test/built-ins/Array/prototype/toReversed/immutable.js
* - test/built-ins/Array/prototype/toReversed/zero-or-one-element.js
* - test/built-ins/Array/prototype/with/immutable.js
* - test/built-ins/Array/prototype/with/index-negative.js
* - test/built-ins/Array/prototype/push/S15.4.4.7_A1_T1.js
* - test/built-ins/Array/prototype/pop/S15.4.4.6_A1.1_T1.js
* - test/built-ins/Array/prototype/shift/S15.4.4.9_A1.1_T1.js
* - test/built-ins/Array/prototype/unshift/S15.4.4.13_A1_T1.js
* - test/built-ins/Array/prototype/splice/S15.4.4.12_A1.1_T1.js
* - test/built-ins/Array/prototype/splice/S15.4.4.12_A1.2_T1.js
* - test/built-ins/Array/prototype/splice/called_with_one_argument.js
* - test/built-ins/Array/prototype/fill/fill-values.js
* - test/built-ins/Array/prototype/fill/fill-values-custom-start-and-end.js
* - test/built-ins/Array/prototype/fill/return-this.js
* - test/built-ins/Array/prototype/copyWithin/non-negative-target-start-and-end.js
* - test/built-ins/Array/prototype/copyWithin/return-this.js
* - test/built-ins/Array/prototype/keys/iteration.js
* - test/built-ins/Array/prototype/values/iteration.js
* - test/built-ins/Array/prototype/entries/iteration.js
* - test/built-ins/Array/isArray/15.4.3.2-0-3.js
* - test/built-ins/Array/isArray/15.4.3.2-0-4.js
* - test/built-ins/Array/from/from-array.js
* - test/built-ins/Array/from/from-string.js
* - test/built-ins/Array/from/array-like-has-length-but-no-indexes-with-values.js
* - test/built-ins/Array/of/creates-a-new-array-from-arguments.js
*
* Copyright (C) 2015 André Bargull. 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) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
* Copyright (C) 2020 Alexey Shvayka. All rights reserved.
* Copyright (C) 2020 Rick Waldron. All rights reserved.
* Copyright (C) 2021 Igalia, S.L. All rights reserved.
* Copyright (c) 2012 Ecma International. All rights reserved.
* Copyright (c) 2014 Hank Yates. All rights reserved.
* Copyright (c) 2015 the V8 project authors. All rights reserved.
* Copyright (c) 2021 Rick Waldron. All rights reserved.
* Copyright 2009 the Sputnik authors. All rights reserved.
* Copyright 2015 Microsoft Corporation. All rights reserved.
* Copyright 2016 The V8 project authors. 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/Array/prototype/includes/samevaluezero.js",
code: `const input = [42, 0, 1, NaN]; return [input.includes(42), input.includes("42"), input.includes([42]), input.includes(true), input.includes(NaN), input.includes(0), input.includes(-0), input.includes(null), input.includes("")]`,
expected: [true, false, false, false, true, true, true, false, false],
},
{
path: "test/built-ins/Array/prototype/includes/using-fromindex.js",
code: `const input = ["a", "b", "c"]; return [input.includes("a", 0), input.includes("a", 1), input.includes("a", -4), input.includes("a", -3), input.includes("a", -2), input.includes("b", 0), input.includes("b", 1), input.includes("b", 2), input.includes("b", -3), input.includes("b", -2), input.includes("b", -1), input.includes("c", 0), input.includes("c", 2), input.includes("c", 3), input.includes("c", -3), input.includes("c", -1)]`,
expected: [true, false, true, true, false, true, true, false, true, true, false, true, true, false, true, true],
},
{
path: "test/built-ins/Array/prototype/join/S15.4.4.5_A3.1_T1.js",
code: `return [[0, 1, 2, 3].join("&"), [0, 1, 2, 3].join("")]`,
expected: ["0&1&2&3", "0123"],
},
{
path: "test/built-ins/Array/prototype/join/S15.4.4.5_A3.2_T1.js",
code: `return [
["", "", ""].join(""),
["&", "&", "&"].join("&"),
[true, true, true].join(),
[null, null, null].join(),
[undefined, undefined, undefined].join(),
[Infinity, Infinity, Infinity].join(),
[NaN, NaN, NaN].join(),
]`,
expected: ["", "&&&&&", "true,true,true", ",,", ",,", "Infinity,Infinity,Infinity", "NaN,NaN,NaN"],
},
{
path: "test/built-ins/Array/prototype/slice/S15.4.4.10_A1.2_T2.js",
code: `return [0, 1, 2, 3, 4].slice(-1, 5)`,
expected: [4],
},
{
path: "test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T1.js",
code: `return [].concat([0, 1], [2, 3, 4])`,
expected: [0, 1, 2, 3, 4],
},
{
path: "test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T2.js",
code: `const object = { value: 1 }; const result = [0].concat(object, [1, 2], -1, true, "NaN"); return [result, result[1] === object]`,
expected: [[0, { value: 1 }, 1, 2, -1, true, "NaN"], true],
},
{
path: "test/built-ins/Array/prototype/concat/S15.4.4.4_A1_T3.js",
code: `const input = [0, 1]; const result = input.concat(); return [result, result !== input]`,
expected: [[0, 1], true],
},
{
path: "test/built-ins/Array/prototype/indexOf/fromindex-zero-conversion.js",
code: `const result = [true].indexOf(true, -0); return [result, 1 / result === Infinity]`,
expected: [0, true],
},
{
path: "test/built-ins/Array/prototype/indexOf/length-zero-returns-minus-one.js",
code: `return [].indexOf(1)`,
expected: -1,
},
{
path: "test/built-ins/Array/prototype/lastIndexOf/fromindex-zero-conversion.js",
code: `const result = [true].lastIndexOf(true, -0); return [result, 1 / result === Infinity]`,
expected: [0, true],
},
{
path: "test/built-ins/Array/prototype/lastIndexOf/length-zero-returns-minus-one.js",
code: `return [].lastIndexOf(1)`,
expected: -1,
},
{
path: "test/built-ins/Array/prototype/at/returns-item.js",
code: `const input = [1, 2, 3, 4, undefined, 5]; return [input.at(0), input.at(1), input.at(2), input.at(3), input.at(4) === undefined, input.at(5)]`,
expected: [1, 2, 3, 4, true, 5],
},
{
path: "test/built-ins/Array/prototype/at/returns-item-relative-index.js",
code: `const input = [1, 2, 3, 4, undefined, 5]; return [input.at(0), input.at(-1), input.at(-2) === undefined, input.at(-3), input.at(-4)]`,
expected: [1, 5, true, 4, 3],
},
{
path: "test/built-ins/Array/prototype/at/returns-undefined-for-out-of-range-index.js",
code: `const input = []; return [input.at(-2) === undefined, input.at(0) === undefined, input.at(1) === undefined]`,
expected: [true, true, true],
},
{
path: "test/built-ins/Array/prototype/flat/null-undefined-elements.js",
code: `const result = [1, [null, [undefined]]].flat(2); return [result.length, result[0], result[1] === null, result[2] === undefined]`,
expected: [3, 1, true, true],
},
{
path: "test/built-ins/Array/prototype/flat/positive-infinity.js",
code: `return [1, [2, [3, [4]]]].flat(Infinity)`,
expected: [1, 2, 3, 4],
},
{
path: "test/built-ins/Array/prototype/reverse/S15.4.4.8_A1_T1.js",
code: `const empty = []; const one = [1]; const input = [1, 2]; const emptyResult = empty.reverse(); const oneResult = one.reverse(); const result = input.reverse(); return [emptyResult === empty, oneResult === one, result === input, input]`,
expected: [true, true, true, [2, 1]],
},
{
path: "test/built-ins/Array/prototype/toReversed/immutable.js",
code: `const input = [0, 1, 2]; const result = input.toReversed(); return [input, result !== input]`,
expected: [[0, 1, 2], true],
},
{
path: "test/built-ins/Array/prototype/toReversed/zero-or-one-element.js",
code: `const zero = []; const one = [1]; const zeroResult = zero.toReversed(); const oneResult = one.toReversed(); return [zeroResult, oneResult, zeroResult !== zero, oneResult !== one]`,
expected: [[], [1], true, true],
},
{
path: "test/built-ins/Array/prototype/with/immutable.js",
code: `const input = [0, 1, 2]; const result = input.with(1, 3); return [input, result !== input, input.with(1, 1) !== input]`,
expected: [[0, 1, 2], true, true],
},
{
path: "test/built-ins/Array/prototype/with/index-negative.js",
code: `const input = [0, 1, 2]; return [input.with(-1, 4), input.with(-3, 4)]`,
expected: [
[0, 1, 4],
[4, 1, 2],
],
},
{
path: "test/built-ins/Array/prototype/push/S15.4.4.7_A1_T1.js",
code: `const input = []; return [input.push(1), input.push(), input.push(-1), input]`,
expected: [1, 1, 2, [1, -1]],
},
{
path: "test/built-ins/Array/prototype/pop/S15.4.4.6_A1.1_T1.js",
code: `const input = []; return [input.pop() === undefined, input.length]`,
expected: [true, 0],
},
{
path: "test/built-ins/Array/prototype/shift/S15.4.4.9_A1.1_T1.js",
code: `const input = []; return [input.shift() === undefined, input.length]`,
expected: [true, 0],
},
{
path: "test/built-ins/Array/prototype/unshift/S15.4.4.13_A1_T1.js",
code: `const input = []; return [input.unshift(1), input[0], input.unshift(), input.unshift(-1), input]`,
expected: [1, 1, 1, 2, [-1, 1]],
},
{
path: "test/built-ins/Array/prototype/splice/S15.4.4.12_A1.1_T1.js",
code: `const input = [0, 1, 2, 3]; const removed = input.splice(0, 3); return [input, removed]`,
expected: [[3], [0, 1, 2]],
},
{
path: "test/built-ins/Array/prototype/splice/S15.4.4.12_A1.2_T1.js",
code: `const input = [0, 1]; const removed = input.splice(-2, -1); return [input, removed]`,
expected: [[0, 1], []],
},
{
path: "test/built-ins/Array/prototype/splice/called_with_one_argument.js",
code: `const input = ["first", "second", "third"]; const removed = input.splice(1); return [input, removed]`,
expected: [["first"], ["second", "third"]],
},
{
path: "test/built-ins/Array/prototype/fill/fill-values-custom-start-and-end.js",
code: `const input = [0, 0, 0, 0, 0]; input.fill(8, -3, 4); const sparse = []; sparse[4] = 0; sparse.fill(8, 1, 3); return [[0, 0, 0].fill(8, 1, 2), input, [0, 0, 0, 0, 0].fill(8, -2, -1), [0, 0, 0, 0, 0].fill(8, -1, -3), [0 in sparse, sparse[1], sparse[2], 3 in sparse, sparse[4]]]`,
expected: [
[0, 8, 0],
[0, 0, 8, 8, 0],
[0, 0, 0, 8, 0],
[0, 0, 0, 0, 0],
[false, 8, 8, false, 0],
],
},
{
path: "test/built-ins/Array/prototype/fill/return-this.js",
code: `const input = []; return input.fill(1) === input`,
expected: true,
},
{
path: "test/built-ins/Array/prototype/fill/fill-values.js",
code: `const omitted = [0, 0].fill(); return [[].fill(8), omitted.map((value) => value === undefined), [0, 0, 0].fill(8)]`,
expected: [[], [true, true], [8, 8, 8]],
},
{
path: "test/built-ins/Array/prototype/copyWithin/non-negative-target-start-and-end.js",
code: `return [[0, 1, 2, 3].copyWithin(0, 0, 0), [0, 1, 2, 3].copyWithin(0, 0, 2), [0, 1, 2, 3].copyWithin(0, 1, 2), [0, 1, 2, 3].copyWithin(1, 0, 2), [0, 1, 2, 3, 4, 5].copyWithin(1, 3, 5)]`,
expected: [
[0, 1, 2, 3],
[0, 1, 2, 3],
[1, 1, 2, 3],
[0, 0, 1, 3],
[0, 3, 4, 3, 4, 5],
],
},
{
path: "test/built-ins/Array/prototype/copyWithin/return-this.js",
code: `const input = [0, 1, 2, 3]; const result = input.copyWithin(1, 0, 2); return [input, result === input]`,
expected: [[0, 0, 1, 3], true],
},
{
path: "test/built-ins/Array/prototype/keys/iteration.js",
code: `return ["a", "b", "c"].keys()`,
expected: [0, 1, 2],
},
{
path: "test/built-ins/Array/prototype/values/iteration.js",
code: `return ["a", "b", "c"].values()`,
expected: ["a", "b", "c"],
},
{
path: "test/built-ins/Array/prototype/entries/iteration.js",
code: `return ["a", "b"].entries()`,
expected: [
[0, "a"],
[1, "b"],
],
},
{
path: "test/built-ins/Array/isArray/15.4.3.2-0-3.js",
code: `return [Array.isArray([]), Array.isArray([1]), Array.isArray(Array.of(1))]`,
expected: [true, true, true],
},
{
path: "test/built-ins/Array/isArray/15.4.3.2-0-4.js",
code: `return [Array.isArray(42), Array.isArray({}), Array.isArray(null), Array.isArray("array")]`,
expected: [false, false, false, false],
},
{
path: "test/built-ins/Array/from/from-array.js",
code: `const input = [0, "foo", undefined, Infinity]; const result = Array.from(input); return [result.length, result[0], result[1], result[2] === undefined, result[3] === Infinity, result !== input, result instanceof Array]`,
expected: [4, 0, "foo", true, true, true, true],
},
{
path: "test/built-ins/Array/from/from-string.js",
code: `return Array.from("Test")`,
expected: ["T", "e", "s", "t"],
},
{
path: "test/built-ins/Array/from/array-like-has-length-but-no-indexes-with-values.js",
code: `const result = Array.from({ length: 5 }); const mapped = result.map(() => 1); return [result.length, result.map((value) => value === undefined), mapped.length, mapped]`,
expected: [5, [true, true, true, true, true], 5, [1, 1, 1, 1, 1]],
},
{
path: "test/built-ins/Array/of/creates-a-new-array-from-arguments.js",
code: `const mixed = Array.of(undefined, false, null, undefined); return [Array.of("Mike", "Rick", "Leo"), mixed.length, mixed[0] === undefined, mixed[1], mixed[2], mixed[3] === undefined, Array.of()]`,
expected: [["Mike", "Rick", "Leo"], 4, true, false, null, true, []],
},
] as const
describe("Test262 Array core adaptations", () => {
for (const item of cases) {
test(item.path, async () => {
expect(await value(item.code)).toEqual(item.expected)
})
}
})

View file

@ -661,6 +661,9 @@ describe("CodeMode public contract", () => {
expect(instructions).toContain("URL, URLSearchParams, and URI encoding helpers")
expect(instructions).not.toContain("host globals")
expect(instructions).toContain("Use Code Mode tools for external operations")
expect(instructions).toContain(
"Prefer explicit `return`; otherwise only the final top-level expression becomes the result.",
)
expect(instructions).toContain(
"Dates and URLs serialize to strings at data boundaries; Map/Set/RegExp/URLSearchParams serialize to `{}`.",
)
@ -1084,6 +1087,24 @@ describe("CodeMode public contract", () => {
expect(Schema.decodeUnknownSync(CodeMode.Result)(JSON.parse(JSON.stringify(result)))).toStrictEqual(result)
})
test("returns the final top-level expression when return is omitted", async () => {
const result = await Effect.runPromise(CodeMode.execute({ code: `1; 2` }))
expect(result).toStrictEqual({ ok: true, value: 2, toolCalls: [] })
})
test("does not implicitly return expressions nested in control flow", async () => {
const result = await Effect.runPromise(CodeMode.execute({ code: `if (true) { 2 }` }))
expect(result).toStrictEqual({ ok: true, value: null, toolCalls: [] })
})
test("returns null when the final top-level statement is not an expression", async () => {
const result = await Effect.runPromise(CodeMode.execute({ code: `1; const value = 2` }))
expect(result).toStrictEqual({ ok: true, value: null, toolCalls: [] })
})
test("rejects invalid configuration and discovery limits", async () => {
expect(() => CodeMode.execute({ code: "return 1", limits: { timeoutMs: 0 } })).toThrow(RangeError)
expect(() => CodeMode.execute({ code: "return 1", limits: { timeoutMs: Number.POSITIVE_INFINITY } })).toThrow(

File diff suppressed because it is too large Load diff

View file

@ -177,13 +177,13 @@ describe("OpenAPI.fromSpec", () => {
const spec = await opencodeSpec()
const result = OpenAPI.fromSpec({ spec, baseUrl })
expect(result.skipped).toHaveLength(5)
expect(result.skipped).toHaveLength(4)
expect(result.skipped).toContainEqual({
method: "GET",
path: "/api/pty/{ptyID}/connect",
reason: "WebSocket operations are not supported",
})
expect(result.skipped.filter((item) => item.reason === "SSE operations are not supported")).toHaveLength(3)
expect(result.skipped.filter((item) => item.reason === "SSE operations are not supported")).toHaveLength(2)
expect(result.skipped).toContainEqual({
method: "GET",
path: "/api/fs/read/*",
@ -210,11 +210,11 @@ describe("OpenAPI.fromSpec", () => {
if (!Tool.isDefinition(instructionPut)) throw new Error("v2.session.instructions.entry.put was not generated")
expect(inputTypeScript(instructionPut)).toBe("{ sessionID: string; key: string; value: unknown }")
expect(toolAt(result.tools, "v2_session_instructions_entry_put_2")).toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connect")).toBeUndefined()
expect(Tool.isDefinition(toolAt(result.tools, "v2.pty.connect"))).toBe(false)
expect(toolAt(result.tools, "v2.session.log")).toBeUndefined()
expect(toolAt(result.tools, "v2.event.subscribe")).toBeUndefined()
expect(toolAt(result.tools, "v2.fs.read")).toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connectToken")).not.toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connect.token")).not.toBeUndefined()
})
test("preserves operation path sanitization and collision handling", () => {

View file

@ -42,11 +42,6 @@ describe("H2: string property access reads as undefined (not a throw)", () => {
test("unknown property on a number is undefined", async () => {
expect(await value(`return (5).foo ?? "n"`)).toBe("n")
})
test("supported string methods still work", async () => {
expect(await value(`return "AB".toLowerCase()`)).toBe("ab")
expect(await value(`return "hello".length`)).toBe(5)
})
})
describe("H3: array property access reads as undefined (not a throw)", () => {
@ -63,8 +58,7 @@ describe("H3: array property access reads as undefined (not a throw)", () => {
expect(await value(`return [1,2,3].toSpliced === undefined`)).toBe(true)
})
test("supported array methods and indexing still work", async () => {
expect(await value(`return [1,2,3].map(x => x + 1)`)).toEqual([2, 3, 4])
test("array indexing still works", async () => {
expect(await value(`return [1,2,3][9] === undefined`)).toBe(true)
expect(await value(`return [1,2,3][9]`)).toBeNull()
})
@ -202,9 +196,6 @@ describe("Error values and instanceof", () => {
"TypeError",
true,
])
expect(await value(`try { "a".normalize("NOPE") } catch (e) { return [e.name, e instanceof RangeError] }`)).toEqual(
["RangeError", true],
)
expect(await value(`try { "a".match("(") } catch (e) { return [e.name, e instanceof SyntaxError] }`)).toEqual([
"SyntaxError",
true,
@ -263,55 +254,18 @@ describe("Error values and instanceof", () => {
})
})
describe("array methods: splice, fill, copyWithin, keys/values/entries", () => {
test("sort and reverse mutate and return the receiver", async () => {
describe("CodeMode-specific array behavior", () => {
test("sort with a comparator mutates and returns the receiver", async () => {
expect(
await value(`
const sorted = [3, 1, 2]
const sortResult = sorted.sort((a, b) => a - b)
const reversed = [1, 2, 3]
const reverseResult = reversed.reverse()
return { sorted, sameSort: sorted === sortResult, reversed, sameReverse: reversed === reverseResult }
const input = [3, 1, 2]
const result = input.sort((a, b) => a - b)
return { input, same: input === result }
`),
).toEqual({ sorted: [1, 2, 3], sameSort: true, reversed: [3, 2, 1], sameReverse: true })
).toEqual({ input: [1, 2, 3], same: true })
})
test("array callbacks receive the receiver and observe later mutations", async () => {
expect(
await value(`
const values = [1, 2, 3]
const seen = values.map((value, index, receiver) => {
if (index === 0) values[1] = 9
return [value, receiver === values]
})
return seen
`),
).toEqual([
[1, true],
[9, true],
[3, true],
])
expect(
await value(`
const values = [1, 2, 3]
const seen = []
values.forEach((value, index) => {
seen.push(value)
if (index === 0) values.pop()
})
return seen
`),
).toEqual([1, 2])
})
test("splice removes in place and returns the removed elements", async () => {
expect(await value(`const a = [1,2,3,4]; const removed = a.splice(1, 2); return { removed, a }`)).toEqual({
removed: [2, 3],
a: [1, 4],
})
})
test("splice inserts new elements at the cut", async () => {
test("splice can replace and insert elements", async () => {
expect(await value(`const a = ["a","d"]; a.splice(1, 0, "b", "c"); return a`)).toEqual(["a", "b", "c", "d"])
expect(await value(`const a = [1,2,3]; const removed = a.splice(1, 1, "x"); return { removed, a }`)).toEqual({
removed: [2],
@ -319,32 +273,12 @@ describe("array methods: splice, fill, copyWithin, keys/values/entries", () => {
})
})
test("splice with one argument removes to the end; negative start counts back", async () => {
expect(await value(`const a = [1,2,3]; const removed = a.splice(1); return { removed, a }`)).toEqual({
removed: [2, 3],
a: [1],
})
expect(await value(`const a = [1,2,3]; const removed = a.splice(-1); return { removed, a }`)).toEqual({
removed: [3],
a: [1, 2],
})
})
test("splice rejects inserting a container into itself", async () => {
const err = await error(`const a = [1]; a.splice(0, 0, [a]); return a`)
expect(err.kind).toBe("InvalidDataValue")
expect(err.message).toContain("circular")
})
test("fill overwrites a range and returns the mutated array", async () => {
expect(await value(`const a = [1,2,3,4]; return a.fill(0, 1, 3)`)).toEqual([1, 0, 0, 4])
expect(await value(`return [1,2,3].fill("z")`)).toEqual(["z", "z", "z"])
})
test("copyWithin copies a range in place", async () => {
expect(await value(`return [1,2,3,4,5].copyWithin(0, 3)`)).toEqual([4, 5, 3, 4, 5])
})
test("keys/values/entries return arrays usable with for...of and spread", async () => {
expect(await value(`return [...["x","y","z"].keys()]`)).toEqual([0, 1, 2])
expect(await value(`return ["x","y"].values()`)).toEqual(["x", "y"])
@ -359,16 +293,9 @@ describe("array methods: splice, fill, copyWithin, keys/values/entries", () => {
})
})
describe("string methods: localeCompare, normalize, trim aliases", () => {
describe("CodeMode-specific string behavior", () => {
test("localeCompare orders strings for sorting", async () => {
expect(await value(`return ["b","a","c"].sort((x, y) => x.localeCompare(y))`)).toEqual(["a", "b", "c"])
expect(await value(`return "a".localeCompare("a")`)).toBe(0)
})
test("normalize applies unicode normalization forms", async () => {
expect(await value(`return "\\u0065\\u0301".normalize("NFC").length`)).toBe(1)
expect(await value(`return "\\u00e9".normalize("NFD").length`)).toBe(2)
expect(await value(`return "x".normalize() === "x"`)).toBe(true)
})
test("an invalid normalize form is a clear catchable error", async () => {
@ -453,11 +380,6 @@ describe("H5: builtin coercion functions work as array callbacks", () => {
expect(await value(`return [1, 2, 3].map(String)`)).toEqual(["1", "2", "3"])
})
test("arrow callbacks still work (no regression)", async () => {
expect(await value(`return [1, 2, 3, 4].filter(x => x % 2 === 0)`)).toEqual([2, 4])
expect(await value(`return [1, 2, 3].reduce((a, b) => a + b, 0)`)).toBe(6)
})
test("a non-callable callback is still rejected", async () => {
const err = await error(`return [1,2,3].map(42)`)
expect(err.message).toContain("callback")

View file

@ -313,6 +313,12 @@ describe("promises at data boundaries", () => {
expect(diagnostic.message).toContain("await tools.ns.tool(...)")
})
test("collection helpers do not let un-awaited promises cross the result boundary", async () => {
const diagnostic = await error(`return Array.from([Promise.resolve(1)])`)
expect(diagnostic.kind).toBe("InvalidDataValue")
expect(diagnostic.message).toContain("un-awaited Promise")
})
test("passing an un-awaited promise as a tool argument is a clear diagnostic", async () => {
const diagnostic = await error(`return await tools.host.sleepy({ id: tools.host.sleepy({ id: 1 }) })`)
expect(diagnostic.kind).toBe("InvalidDataValue")

View file

@ -19,6 +19,28 @@ const error = async (code: string) => {
return result.error
}
describe("Number and Math", () => {
test("Math.random returns a number in [0, 1)", async () => {
expect(await value(`const n = Math.random(); return typeof n === "number" && n >= 0 && n < 1`)).toBe(true)
})
test("Number exposes native non-finite constants", async () => {
expect(
await value(
`return [Number.isNaN(Number.NaN), Number.POSITIVE_INFINITY === Infinity, Number.NEGATIVE_INFINITY === -Infinity]`,
),
).toEqual([true, true, true])
})
test("Number valueOf returns its primitive receiver", async () => {
expect(await value(`return (42).valueOf()`)).toBe(42)
})
test("Number valueOf does not enable boxed numbers", async () => {
expect((await error(`return new Number(42)`)).kind).toBe("UnsupportedSyntax")
})
})
describe("Date", () => {
test("Date.now() returns a number", async () => {
expect(await value(`return typeof Date.now()`)).toBe("number")
@ -132,9 +154,7 @@ describe("RegExp", () => {
).toEqual(["1", "22"])
})
test("string match: non-global carries index, global lists all matches", async () => {
expect(await value(`const m = "a1b22".match(/\\d+/); return [m[0], m.index]`)).toEqual(["1", 1])
expect(await value(`return "a1b22".match(/\\d+/g)`)).toEqual(["1", "22"])
test("an unmatched string pattern returns null", async () => {
expect(await value(`return "abc".match(/\\d/)`)).toBeNull()
})
@ -142,13 +162,6 @@ describe("RegExp", () => {
expect(await value(`return "a1b22".matchAll(/(\\d+)/g).map((m) => m[1])`)).toEqual(["1", "22"])
})
test("replace and replaceAll with patterns and $1 substitution", async () => {
expect(await value(`return "a1b2".replace(/\\d/, "#")`)).toBe("a#b2")
expect(await value(`return "a1b2".replace(/\\d/g, "#")`)).toBe("a#b#")
expect(await value(`return "a1b2".replaceAll(/\\d/g, "#")`)).toBe("a#b#")
expect(await value(`return "hi bob".replace(/b(o)b/, "[$1]")`)).toBe("hi [o]")
})
test("function replacers receive captures, offsets, input, and named groups", async () => {
expect(
await value(`
@ -214,12 +227,6 @@ describe("RegExp", () => {
expect(await value(`try { "a".replaceAll(/a/, "b"); return "no" } catch { return "caught" }`)).toBe("caught")
})
test("split and search accept patterns", async () => {
expect(await value(`return "a1b22c".split(/\\d+/)`)).toEqual(["a", "b", "c"])
expect(await value(`return "ab42".search(/\\d/)`)).toBe(2)
expect(await value(`return "ab".search(/\\d/)`)).toBe(-1)
})
test("new RegExp constructs from strings; invalid patterns are catchable", async () => {
expect(await value(`return new RegExp("a+", "i").test("AAA")`)).toBe(true)
expect(await value(`try { new RegExp("("); return "no" } catch { return "caught" }`)).toBe("caught")
@ -628,9 +635,7 @@ describe("stdlib integration", () => {
true,
)
expect(
await value(
`try { Object.fromEntries(new Map([["fn", Math.max]])); return false } catch { return true }`,
),
await value(`try { Object.fromEntries(new Map([["fn", Math.max]])); return false } catch { return true }`),
).toBe(true)
})
@ -751,6 +756,43 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
)
})
test("Object.values/entries preserve nested object identity", async () => {
expect(
await value(`
const child = { selected: false }
const rows = { a: child }
Object.values(rows)[0].selected = true
return child.selected
`),
).toBe(true)
expect(
await value(`
const child = { selected: false }
const rows = { a: child }
Object.entries(rows)[0][1].selected = true
return child.selected
`),
).toBe(true)
})
test("Object enumeration preserves promises and callable references", async () => {
expect(
await value(`
const pending = Promise.resolve(1)
const source = { pending }
return [Object.keys(source), Object.hasOwn(source, "pending"), await Object.values(source)[0], await Object.entries(source)[0][1]]
`),
).toEqual([["pending"], true, 1, 1])
expect(await value(`return Object.values({ max: Math.max })[0](1, 2)`)).toBe(2)
})
test("Object enumeration rejects invalid receivers and gives promises an await hint", async () => {
const diagnostic = await error(`return Object.keys(Promise.resolve({ a: 1 }))`)
expect(diagnostic.kind).toBe("InvalidDataValue")
expect(diagnostic.message).toContain("await")
expect((await error(`return Object.keys(Math)`)).kind).toBe("InvalidDataValue")
})
test("Object.assign keeps Maps usable", async () => {
expect(await value(`const merged = Object.assign({}, { m: new Map([["a", 1]]) }); return merged.m.get("a")`)).toBe(
1,
@ -773,6 +815,53 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
expect(await value(`return Array.from([new Date(5)])[0].getTime()`)).toBe(5)
})
test("Array.from and Array.of preserve nested object identity", async () => {
expect(
await value(`
const child = { selected: false }
Array.from([child])[0].selected = true
return child.selected
`),
).toBe(true)
expect(
await value(`
const child = { selected: false }
Array.of(child)[0].selected = true
return child.selected
`),
).toBe(true)
})
test("Array.from and Array.of preserve promises and callable references", async () => {
expect(
await value(`
const pending = Promise.resolve(1)
return [await Array.from([pending])[0], await Array.of(pending)[0]]
`),
).toEqual([1, 1])
expect(await value(`return [Array.from([Math.max])[0](1, 2), Array.of(Math.max)[0](3, 4)]`)).toEqual([2, 4])
})
test("Array.from preserves identity across supported collection shapes", async () => {
expect(
await value(`
const child = { selected: false }
const fromArrayLike = Array.from({ 0: child, length: 1 })
const fromMap = Array.from(new Map([["child", child]]))
const fromSet = Array.from(new Set([child]))
fromArrayLike[0].selected = true
return [fromMap[0][1] === child, fromSet[0] === child, child.selected]
`),
).toEqual([true, true, true])
})
test("Array.from rejects invalid receivers and gives promises an await hint", async () => {
const diagnostic = await error(`return Array.from(Promise.resolve([1]))`)
expect(diagnostic.kind).toBe("InvalidDataValue")
expect(diagnostic.message).toContain("await")
expect((await error(`return Array.from(() => 1)`)).kind).toBe("InvalidDataValue")
})
test("regexes stay callable through Object.values", async () => {
expect(await value(`return Object.values({ r: /ab+/ })[0].test("abb")`)).toBe(true)
})

View file

@ -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<Argument>
expected?: string | number
outcome?: Outcome
}
type Vector = {
path: string
method: string
static?: boolean
assertions: ReadonlyArray<Assertion>
}
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<Vector> = []
const add = (path: string, method: string, assertions: ReadonlyArray<Assertion>, staticMethod = false) => {
vectors.push({ path, method, assertions, static: staticMethod })
}
const assertion = (label: string, input: string, expected: string | number, args: ReadonlyArray<Argument> = []) => ({
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)
})
}
})

View file

@ -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<Vector>) => {
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("(?<named>b)", "g"), "($<named>)"),
"abcba".replaceAll(new RegExp("(?<named>b)", "g"), "($<named)"),
"abcba".replaceAll(new RegExp("(?<named>b)", "g"), "($<unnamed>)"),
"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(?<z>b)(ca)", "g"), "($$<z>)"),
"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($<named)c($<named)a", "a()c()a", "($)bc($)bc",
"($)bc($)bc", "($$)bc($$)bc", "($$)bc($$)bc", "($&)bc($&)bc", "($1)bc($1)bc",
"($`)bc($`)bc", "($')bc($')bc", "($<z>)bc($<z>)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],
},
])

View file

@ -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))
})
}
})

View file

@ -0,0 +1,77 @@
# Test262 Array Coverage
The Array tests adapt Test262 at revision `250f204f23a9249ff204be2baec29600faae7b75`. They cover CodeMode's 35
exposed instance methods and three static methods using actual arrays, accepted argument types, deterministic behavior,
and CodeMode's materialized collection conventions. Each executable case names its exact upstream source path.
`LICENSE.test262` contains the upstream BSD terms.
This is coverage of CodeMode's bounded Array 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 38 relevant upstream API directories contain 2,837 files. The executable suite adapts assertions from 83 distinct
sources.
| API | Upstream files | Adapted sources |
| ------------------------------- | -------------: | --------------: |
| `Array.prototype.map` | 216 | 3 |
| `Array.prototype.filter` | 242 | 3 |
| `Array.prototype.find` | 23 | 4 |
| `Array.prototype.findIndex` | 23 | 3 |
| `Array.prototype.findLast` | 24 | 3 |
| `Array.prototype.findLastIndex` | 24 | 3 |
| `Array.prototype.some` | 219 | 2 |
| `Array.prototype.every` | 218 | 2 |
| `Array.prototype.includes` | 30 | 2 |
| `Array.prototype.join` | 23 | 2 |
| `Array.prototype.reduce` | 260 | 3 |
| `Array.prototype.reduceRight` | 260 | 3 |
| `Array.prototype.flatMap` | 24 | 2 |
| `Array.prototype.forEach` | 190 | 2 |
| `Array.prototype.sort` | 54 | 3 |
| `Array.prototype.toSorted` | 21 | 4 |
| `Array.prototype.slice` | 71 | 1 |
| `Array.prototype.concat` | 69 | 3 |
| `Array.prototype.indexOf` | 201 | 2 |
| `Array.prototype.lastIndexOf` | 198 | 2 |
| `Array.prototype.at` | 13 | 3 |
| `Array.prototype.flat` | 19 | 2 |
| `Array.prototype.reverse` | 18 | 1 |
| `Array.prototype.toReversed` | 17 | 2 |
| `Array.prototype.with` | 21 | 2 |
| `Array.prototype.push` | 24 | 1 |
| `Array.prototype.pop` | 23 | 1 |
| `Array.prototype.shift` | 20 | 1 |
| `Array.prototype.unshift` | 22 | 1 |
| `Array.prototype.splice` | 81 | 3 |
| `Array.prototype.fill` | 22 | 3 |
| `Array.prototype.copyWithin` | 39 | 2 |
| `Array.prototype.keys` | 12 | 1 |
| `Array.prototype.values` | 12 | 1 |
| `Array.prototype.entries` | 12 | 1 |
| `Array.from` | 47 | 3 |
| `Array.isArray` | 29 | 2 |
| `Array.of` | 16 | 1 |
## Exclusions
Assertions are not adapted when they test behavior outside CodeMode's documented Array surface:
- Function metadata, property descriptors, constructibility, prototype mutation, species constructors, or cross-realm
identity.
- Generic receivers, detached methods, `.call`, `.apply`, boxed values, custom coercion objects, Symbols, BigInts,
proxies, accessors, frozen arrays, typed arrays, or ArrayBuffers.
- `Array.from` mappers, custom iterables, constructor substitution, and iterator-closing behavior.
- Native iterator identity, `.next()`, completion records, or live iterator mutation. CodeMode deliberately materializes
`keys`, `values`, and `entries` as arrays.
- Sparse-array assertions that depend on literal elisions or inherited indexed properties. CodeMode's confined data
model does not preserve those prototype and hole semantics at every boundary.
- Argument coercions outside the accepted schema-like surface. Numeric positions must be numbers and `join` separators
must be strings.
- Exact native error brands where CodeMode exposes a safe runtime error instead.
- Async/effectful callbacks, circular-data rejection, sandbox-value identity, diagnostics, and host-boundary behavior.
Those remain covered by CodeMode-specific tests.
Handwritten tests remain where they specify CodeMode behavior rather than ordinary ECMAScript Array semantics.

View file

@ -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.