use super::{eval, eval_result};
use tsrun::JsValue;
use tsrun::value::JsString;
#[test]
fn test_array() {
assert_eq!(eval("const arr = [1, 2, 3]; arr[1]"), JsValue::Number(2.0));
}
#[test]
fn test_debug_map_step_by_step() {
assert_eq!(
eval("let r = 0; [1].map(x => { r = x * 2; }); r"),
JsValue::Number(2.0),
"Callback should be called and set r"
);
let result = eval("[1].map(x => x * 2)");
println!("map result: {:?}", result);
assert_eq!(
eval("[1].map(x => x * 2).length"),
JsValue::Number(1.0),
"Mapped array should have length 1"
);
}
#[test]
fn test_array_push_single() {
assert_eq!(
eval("const arr = [1, 2]; arr.push(3); arr.length"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_push_returns_length() {
assert_eq!(
eval("const arr = [1, 2]; arr.push(3)"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_push_multiple() {
assert_eq!(
eval("const arr = [1]; arr.push(2, 3, 4); arr.length"),
JsValue::Number(4.0)
);
}
#[test]
fn test_array_push_multiple_element_access() {
assert_eq!(
eval("const arr: number[] = []; arr.push(1, 2, 3); arr[0]"),
JsValue::Number(1.0)
);
assert_eq!(
eval("const arr: number[] = []; arr.push(1, 2, 3); arr[1]"),
JsValue::Number(2.0)
);
assert_eq!(
eval("const arr: number[] = []; arr.push(1, 2, 3); arr[2]"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_push_multiple_sum() {
assert_eq!(
eval(
r#"
const arr: number[] = [];
arr.push(1, 2, 3);
let sum: number = 0;
for (let i = 0; i < arr.length; i++) {
sum = sum + arr[i];
}
sum
"#
),
JsValue::Number(6.0)
);
}
#[test]
fn test_array_push_objects_multiple() {
assert_eq!(
eval(
r#"
interface Node { id: number; edges: Node[]; }
const n1: Node = { id: 1, edges: [] };
const n2: Node = { id: 2, edges: [] };
const n3: Node = { id: 3, edges: [] };
n1.edges.push(n2, n3);
n1.edges[0].id + n1.edges[1].id
"#
),
JsValue::Number(5.0)
);
}
#[test]
fn test_gc_cycles_test6_minimal() {
assert_eq!(
eval(
r#"
let sum: number = 0;
for (let i = 0; i < 1000; i++) {
interface GraphNode { id: number; edges: GraphNode[]; }
const n1: GraphNode = { id: 1, edges: [] };
const n2: GraphNode = { id: 2, edges: [] };
const n3: GraphNode = { id: 3, edges: [] };
const n4: GraphNode = { id: 4, edges: [] };
const n5: GraphNode = { id: 5, edges: [] };
n1.edges.push(n2, n3);
n2.edges.push(n1, n3, n4);
n3.edges.push(n2, n4, n5);
n4.edges.push(n3, n5);
n5.edges.push(n4, n1);
sum = sum + n1.id + n2.id + n3.id + n4.id + n5.id;
}
sum
"#
),
JsValue::Number(15000.0)
);
}
#[test]
fn test_gc_cycles_test7_minimal() {
assert_eq!(
eval(
r#"
interface ArrayNode { value: number; refs: ArrayNode[]; }
let sum: number = 0;
for (let i = 0; i < 2; i++) {
const a: ArrayNode = { value: 1, refs: [] };
const b: ArrayNode = { value: 2, refs: [] };
const c: ArrayNode = { value: 3, refs: [] };
a.refs.push(b, c);
b.refs.push(c, a);
c.refs.push(a, b);
sum = sum + a.value + b.value + c.value;
}
sum
"#
),
JsValue::Number(12.0)
);
}
#[test]
fn test_gc_cycles_all_tests() {
assert_eq!(
eval(
r#"
const results: number[] = [];
// Test 1: Two-node cycles
{
let sum: number = 0;
for (let i = 0; i < 100; i++) {
const a: { id: number; other: any } = { id: i, other: null };
const b: { id: number; other: any } = { id: i + 1, other: null };
a.other = b;
b.other = a;
sum = sum + a.id + b.id;
}
results.push(sum);
}
// Test 6: Complex graph with multiple cycles
{
let sum: number = 0;
for (let i = 0; i < 100; i++) {
interface GraphNode { id: number; edges: GraphNode[]; }
const n1: GraphNode = { id: 1, edges: [] };
const n2: GraphNode = { id: 2, edges: [] };
const n3: GraphNode = { id: 3, edges: [] };
const n4: GraphNode = { id: 4, edges: [] };
const n5: GraphNode = { id: 5, edges: [] };
n1.edges.push(n2, n3);
n2.edges.push(n1, n3, n4);
n3.edges.push(n2, n4, n5);
n4.edges.push(n3, n5);
n5.edges.push(n4, n1);
sum = sum + n1.id + n2.id + n3.id + n4.id + n5.id;
}
results.push(sum);
}
// Test 7: Cycles through arrays
{
let sum: number = 0;
for (let i = 0; i < 100; i++) {
interface ArrayNode { value: number; refs: ArrayNode[]; }
const a: ArrayNode = { value: 1, refs: [] };
const b: ArrayNode = { value: 2, refs: [] };
const c: ArrayNode = { value: 3, refs: [] };
a.refs.push(b, c);
b.refs.push(c, a);
c.refs.push(a, b);
sum = sum + a.value + b.value + c.value;
}
results.push(sum);
}
// Return test 6 result as check (should be 1500)
results[1]
"#
),
JsValue::Number(1500.0)
);
}
#[test]
fn test_array_push_element_access() {
assert_eq!(
eval("const arr = [1, 2]; arr.push(3); arr[2]"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_pop_returns_last() {
assert_eq!(
eval("const arr = [1, 2, 3]; arr.pop()"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_pop_modifies_length() {
assert_eq!(
eval("const arr = [1, 2, 3]; arr.pop(); arr.length"),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_pop_empty() {
assert_eq!(eval("const arr = []; arr.pop()"), JsValue::Undefined);
}
#[test]
fn test_array_map_double() {
assert_eq!(
eval("const arr = [1, 2, 3].map(x => x * 2); arr[0]"),
JsValue::Number(2.0)
);
assert_eq!(
eval("const arr = [1, 2, 3].map(x => x * 2); arr[1]"),
JsValue::Number(4.0)
);
assert_eq!(
eval("const arr = [1, 2, 3].map(x => x * 2); arr[2]"),
JsValue::Number(6.0)
);
}
#[test]
fn test_array_map_preserves_length() {
assert_eq!(
eval("[1, 2, 3].map(x => x * 2).length"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_map_with_index() {
assert_eq!(
eval("[10, 20, 30].map((x, i) => i)[1]"),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_map_to_strings() {
assert_eq!(
eval("[1, 2, 3].map(x => 'n' + x)[0]"),
JsValue::String(JsString::from("n1"))
);
}
#[test]
fn test_array_filter_evens() {
assert_eq!(
eval("[1, 2, 3, 4].filter(x => x % 2 === 0).length"),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_filter_values() {
assert_eq!(
eval("[1, 2, 3, 4].filter(x => x % 2 === 0)[0]"),
JsValue::Number(2.0)
);
assert_eq!(
eval("[1, 2, 3, 4].filter(x => x % 2 === 0)[1]"),
JsValue::Number(4.0)
);
}
#[test]
fn test_array_filter_none_match() {
assert_eq!(
eval("[1, 2, 3].filter(x => x > 10).length"),
JsValue::Number(0.0)
);
}
#[test]
fn test_array_filter_all_match() {
assert_eq!(
eval("[1, 2, 3].filter(x => x > 0).length"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_filter_with_index() {
assert_eq!(
eval("[10, 20, 30, 40].filter((x, i) => i % 2 === 0).length"),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_map_filter_chain() {
assert_eq!(
eval("[1, 2, 3, 4].map(x => x * 2).filter(x => x > 4).length"),
JsValue::Number(2.0)
);
assert_eq!(
eval("[1, 2, 3, 4].map(x => x * 2).filter(x => x > 4)[0]"),
JsValue::Number(6.0)
);
}
#[test]
fn test_array_foreach_side_effect() {
assert_eq!(
eval("let sum = 0; [1, 2, 3].forEach(x => sum += x); sum"),
JsValue::Number(6.0)
);
}
#[test]
fn test_array_foreach_returns_undefined() {
assert_eq!(eval("[1, 2, 3].forEach(x => x * 2)"), JsValue::Undefined);
}
#[test]
fn test_array_foreach_with_index() {
assert_eq!(
eval("let result = 0; [10, 20, 30].forEach((x, i) => result += i); result"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_reduce_sum() {
assert_eq!(
eval("[1, 2, 3, 4].reduce((acc, x) => acc + x, 0)"),
JsValue::Number(10.0)
);
}
#[test]
fn test_array_reduce_no_initial() {
assert_eq!(
eval("[1, 2, 3, 4].reduce((acc, x) => acc + x)"),
JsValue::Number(10.0)
);
}
#[test]
fn test_array_reduce_multiply() {
assert_eq!(
eval("[1, 2, 3, 4].reduce((acc, x) => acc * x, 1)"),
JsValue::Number(24.0)
);
}
#[test]
fn test_array_reduce_with_index() {
assert_eq!(
eval("[10, 20, 30].reduce((acc, x, i) => acc + i, 0)"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_reduce_to_object() {
assert_eq!(
eval(
"const obj = [['a', 1], ['b', 2]].reduce((acc, [k, v]) => { acc[k] = v; return acc; }, {}); obj.a"
),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_find_found() {
assert_eq!(eval("[1, 2, 3, 4].find(x => x > 2)"), JsValue::Number(3.0));
}
#[test]
fn test_array_find_not_found() {
assert_eq!(eval("[1, 2, 3].find(x => x > 10)"), JsValue::Undefined);
}
#[test]
fn test_array_find_with_index() {
assert_eq!(
eval("[10, 20, 30].find((x, i) => i === 1)"),
JsValue::Number(20.0)
);
}
#[test]
fn test_array_findindex_found() {
assert_eq!(
eval("[1, 2, 3, 4].findIndex(x => x > 2)"),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_findindex_not_found() {
assert_eq!(
eval("[1, 2, 3].findIndex(x => x > 10)"),
JsValue::Number(-1.0)
);
}
#[test]
fn test_array_findindex_first() {
assert_eq!(
eval("[5, 10, 15].findIndex(x => x >= 5)"),
JsValue::Number(0.0)
);
}
#[test]
fn test_array_indexof_found() {
assert_eq!(eval("[1, 2, 3, 4].indexOf(3)"), JsValue::Number(2.0));
}
#[test]
fn test_array_indexof_not_found() {
assert_eq!(eval("[1, 2, 3].indexOf(5)"), JsValue::Number(-1.0));
}
#[test]
fn test_array_indexof_first_occurrence() {
assert_eq!(eval("[1, 2, 3, 2, 1].indexOf(2)"), JsValue::Number(1.0));
}
#[test]
fn test_array_indexof_from_index() {
assert_eq!(eval("[1, 2, 3, 2, 1].indexOf(2, 2)"), JsValue::Number(3.0));
}
#[test]
fn test_array_includes_found() {
assert_eq!(eval("[1, 2, 3].includes(2)"), JsValue::Boolean(true));
}
#[test]
fn test_array_includes_not_found() {
assert_eq!(eval("[1, 2, 3].includes(5)"), JsValue::Boolean(false));
}
#[test]
fn test_array_includes_from_index() {
assert_eq!(eval("[1, 2, 3].includes(1, 1)"), JsValue::Boolean(false));
}
#[test]
fn test_array_slice_basic() {
assert_eq!(
eval("[1, 2, 3, 4, 5].slice(1, 4).length"),
JsValue::Number(3.0)
);
assert_eq!(eval("[1, 2, 3, 4, 5].slice(1, 4)[0]"), JsValue::Number(2.0));
}
#[test]
fn test_array_slice_no_args() {
assert_eq!(eval("[1, 2, 3].slice().length"), JsValue::Number(3.0));
}
#[test]
fn test_array_slice_negative() {
assert_eq!(
eval("[1, 2, 3, 4, 5].slice(-2).length"),
JsValue::Number(2.0)
);
assert_eq!(eval("[1, 2, 3, 4, 5].slice(-2)[0]"), JsValue::Number(4.0));
}
#[test]
fn test_array_slice_start_only() {
assert_eq!(eval("[1, 2, 3, 4].slice(2).length"), JsValue::Number(2.0));
}
#[test]
fn test_array_concat_arrays() {
assert_eq!(eval("[1, 2].concat([3, 4]).length"), JsValue::Number(4.0));
assert_eq!(eval("[1, 2].concat([3, 4])[2]"), JsValue::Number(3.0));
}
#[test]
fn test_array_concat_values() {
assert_eq!(eval("[1, 2].concat(3, 4).length"), JsValue::Number(4.0));
}
#[test]
fn test_array_concat_mixed() {
assert_eq!(
eval("[1].concat([2, 3], 4, [5]).length"),
JsValue::Number(5.0)
);
}
#[test]
fn test_array_concat_is_concat_spreadable_false() {
assert_eq!(
eval(
r#"
const arr = [1, 2, 3];
arr[Symbol.isConcatSpreadable] = false;
[].concat(arr).length
"#
),
JsValue::Number(1.0) );
}
#[test]
fn test_array_concat_is_concat_spreadable_true() {
assert_eq!(
eval(
r#"
const obj = { 0: 'a', 1: 'b', length: 2, [Symbol.isConcatSpreadable]: true };
[].concat(obj).length
"#
),
JsValue::Number(2.0)
);
assert_eq!(
eval(
r#"
const obj = { 0: 'a', 1: 'b', length: 2, [Symbol.isConcatSpreadable]: true };
[].concat(obj)[0]
"#
),
JsValue::from("a")
);
}
#[test]
fn test_array_concat_non_array_without_spreadable() {
assert_eq!(
eval(
r#"
const obj = { 0: 'a', 1: 'b', length: 2 };
[].concat(obj).length
"#
),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_join_default() {
assert_eq!(
eval("[1, 2, 3].join()"),
JsValue::String(JsString::from("1,2,3"))
);
}
#[test]
fn test_array_join_custom_separator() {
assert_eq!(
eval("[1, 2, 3].join('-')"),
JsValue::String(JsString::from("1-2-3"))
);
}
#[test]
fn test_array_join_empty() {
assert_eq!(
eval("[1, 2, 3].join('')"),
JsValue::String(JsString::from("123"))
);
}
#[test]
fn test_array_every_all_pass() {
assert_eq!(
eval("[2, 4, 6].every(x => x % 2 === 0)"),
JsValue::Boolean(true)
);
}
#[test]
fn test_array_every_some_fail() {
assert_eq!(
eval("[2, 3, 6].every(x => x % 2 === 0)"),
JsValue::Boolean(false)
);
}
#[test]
fn test_array_every_empty() {
assert_eq!(eval("[].every(x => false)"), JsValue::Boolean(true));
}
#[test]
fn test_array_some_one_passes() {
assert_eq!(eval("[1, 2, 3].some(x => x > 2)"), JsValue::Boolean(true));
}
#[test]
fn test_array_some_none_pass() {
assert_eq!(eval("[1, 2, 3].some(x => x > 10)"), JsValue::Boolean(false));
}
#[test]
fn test_array_some_empty() {
assert_eq!(eval("[].some(x => true)"), JsValue::Boolean(false));
}
#[test]
fn test_array_shift() {
assert_eq!(eval("let a = [1, 2, 3]; a.shift()"), JsValue::Number(1.0));
assert_eq!(
eval("let a = [1, 2, 3]; a.shift(); a.length"),
JsValue::Number(2.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.shift(); a[0]"),
JsValue::Number(2.0)
);
assert_eq!(eval("let a = []; a.shift()"), JsValue::Undefined);
}
#[test]
fn test_array_unshift() {
assert_eq!(
eval("let a = [1, 2, 3]; a.unshift(0)"),
JsValue::Number(4.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.unshift(0); a[0]"),
JsValue::Number(0.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.unshift(-1, 0); a.length"),
JsValue::Number(5.0)
);
}
#[test]
fn test_array_reverse() {
assert_eq!(
eval("let a = [1, 2, 3]; a.reverse(); a[0]"),
JsValue::Number(3.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.reverse(); a[2]"),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_sort() {
assert_eq!(
eval("let a = [3, 1, 2]; a.sort(); a[0]"),
JsValue::Number(1.0)
);
assert_eq!(
eval("let a = [3, 1, 2]; a.sort(); a[2]"),
JsValue::Number(3.0)
);
assert_eq!(
eval("let a = ['c', 'a', 'b']; a.sort(); a[0]"),
JsValue::String(JsString::from("a"))
);
assert_eq!(
eval("let a = [3, 1, 2]; a.sort((a, b) => b - a); a[0]"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_fill() {
assert_eq!(
eval("let a = [1, 2, 3]; a.fill(0); a[1]"),
JsValue::Number(0.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.fill(0, 1); a[0]"),
JsValue::Number(1.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.fill(0, 1); a[1]"),
JsValue::Number(0.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.fill(0, 1, 2); a[2]"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_copywithin() {
assert_eq!(
eval("let a = [1, 2, 3, 4, 5]; a.copyWithin(0, 3); a[0]"),
JsValue::Number(4.0)
);
assert_eq!(
eval("let a = [1, 2, 3, 4, 5]; a.copyWithin(0, 3); a[1]"),
JsValue::Number(5.0)
);
}
#[test]
fn test_array_splice() {
assert_eq!(
eval("let a = [1, 2, 3]; let r = a.splice(1, 1); r[0]"),
JsValue::Number(2.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.splice(1, 1); a.length"),
JsValue::Number(2.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.splice(1, 1, 'a', 'b'); a.length"),
JsValue::Number(4.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; a.splice(1, 1, 'a', 'b'); a[1]"),
JsValue::String(JsString::from("a"))
);
}
#[test]
fn test_array_of() {
assert_eq!(eval("Array.of(1, 2, 3).length"), JsValue::Number(3.0));
assert_eq!(eval("Array.of(1, 2, 3)[0]"), JsValue::Number(1.0));
assert_eq!(eval("Array.of(7).length"), JsValue::Number(1.0));
assert_eq!(eval("Array.of().length"), JsValue::Number(0.0));
}
#[test]
fn test_array_from() {
assert_eq!(eval("Array.from([1, 2, 3]).length"), JsValue::Number(3.0));
assert_eq!(eval("Array.from([1, 2, 3])[1]"), JsValue::Number(2.0));
assert_eq!(
eval("Array.from([1, 2, 3], x => x * 2)[1]"),
JsValue::Number(4.0)
);
}
#[test]
fn test_array_at() {
assert_eq!(eval("[1, 2, 3].at(0)"), JsValue::Number(1.0));
assert_eq!(eval("[1, 2, 3].at(2)"), JsValue::Number(3.0));
assert_eq!(eval("[1, 2, 3].at(-1)"), JsValue::Number(3.0));
assert_eq!(eval("[1, 2, 3].at(-2)"), JsValue::Number(2.0));
assert_eq!(eval("[1, 2, 3].at(5)"), JsValue::Undefined);
}
#[test]
fn test_array_at_valueof() {
assert_eq!(
eval(
r#"
let a = [0, 1, 2, 3];
let index = { valueOf() { return 1; } };
a.at(index);
"#
),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_at_symbol_throws() {
let result = eval_result("[1, 2, 3].at(Symbol())");
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("TypeError") || err.to_string().contains("Symbol"));
}
#[test]
fn test_array_at_function_length() {
assert_eq!(eval("Array.prototype.at.length"), JsValue::Number(1.0));
}
#[test]
fn test_array_at_function_name() {
assert_eq!(
eval("Array.prototype.at.name"),
JsValue::String(JsString::from("at"))
);
}
#[test]
fn test_function_call_bind() {
assert_eq!(
eval(
r#"
var __hasOwnProperty = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
__hasOwnProperty({a: 1}, 'a');
"#
),
JsValue::Boolean(true)
);
}
#[test]
fn test_get_own_property_descriptor_function_length() {
assert_eq!(
eval(
r#"
var desc = Object.getOwnPropertyDescriptor(Array.prototype.at, 'length');
desc.value;
"#
),
JsValue::Number(1.0)
);
}
#[test]
fn test_function_length_descriptor_attributes() {
assert_eq!(
eval(
r#"
var desc = Object.getOwnPropertyDescriptor(Array.prototype.at, 'length');
[desc.writable, desc.enumerable, desc.configurable].join(',');
"#
),
JsValue::String(JsString::from("false,false,true"))
);
}
#[test]
fn test_function_name_descriptor_attributes() {
assert_eq!(
eval(
r#"
var desc = Object.getOwnPropertyDescriptor(Array.prototype.at, 'name');
[desc.value, desc.writable, desc.enumerable, desc.configurable].join(',');
"#
),
JsValue::String(JsString::from("at,false,false,true"))
);
}
#[test]
fn test_verify_property_pattern() {
assert_eq!(
eval(
r#"
// Capture primordials like propertyHelper.js does
var __getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
var __hasOwnProperty = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
// Verify length property like the test does
var obj = Array.prototype.at;
var name = "length";
var originalDesc = __getOwnPropertyDescriptor(obj, name);
// Return the result
[
originalDesc.value,
originalDesc.writable,
originalDesc.enumerable,
originalDesc.configurable
].join(',');
"#
),
JsValue::String(JsString::from("1,false,false,true"))
);
}
#[test]
fn test_full_verify_property_simulation() {
assert_eq!(
eval(
r#"
// From propertyHelper.js
var __hasOwnProperty = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
var __getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
function verifyProperty(obj, name, desc) {
var originalDesc = __getOwnPropertyDescriptor(obj, name);
var nameStr = String(name);
if (!__hasOwnProperty(obj, name)) {
throw new Error("obj should have own property " + nameStr);
}
if (__hasOwnProperty(desc, 'value')) {
if (desc.value !== originalDesc.value) {
throw new Error("Expected " + nameStr + " to have value " + desc.value + " but got " + originalDesc.value);
}
}
return true;
}
// The actual test
verifyProperty(Array.prototype.at, "length", { value: 1 });
"#
),
JsValue::Boolean(true)
);
}
#[test]
fn test_assert_samevalue_pattern() {
assert_eq!(
eval(
r#"
// From assert.js
function assert(mustBeTrue, message) {
if (mustBeTrue === true) return;
throw new Error(message || 'Expected true');
}
assert._isSameValue = function(a, b) {
if (a === b) {
return a !== 0 || 1 / a === 1 / b;
}
return a !== a && b !== b;
};
assert.sameValue = function(actual, expected, message) {
if (assert._isSameValue(actual, expected)) return;
throw new Error(message || 'Expected same value');
};
// Test what the test262 test does
assert.sameValue(typeof Array.prototype.at, 'function');
"passed";
"#
),
JsValue::String(JsString::from("passed"))
);
}
#[test]
fn test_array_map_call() {
assert_eq!(
eval(
r#"
var result = Array.prototype.map.call([1, 2, 3], String).join(', ');
result;
"#
),
JsValue::String(JsString::from("1, 2, 3"))
);
}
#[test]
fn test_array_lastindexof() {
assert_eq!(eval("[1, 2, 3, 2, 1].lastIndexOf(2)"), JsValue::Number(3.0));
assert_eq!(eval("[1, 2, 3].lastIndexOf(4)"), JsValue::Number(-1.0));
}
#[test]
fn test_array_reduceright() {
assert_eq!(
eval("[1, 2, 3].reduceRight((acc, x) => acc + x, 0)"),
JsValue::Number(6.0)
);
assert_eq!(
eval("['a', 'b', 'c'].reduceRight((acc, x) => acc + x, '')"),
JsValue::String(JsString::from("cba"))
);
}
#[test]
fn test_array_flat() {
assert_eq!(eval("[[1, 2], [3, 4]].flat()[0]"), JsValue::Number(1.0));
assert_eq!(eval("[[1, 2], [3, 4]].flat().length"), JsValue::Number(4.0));
assert_eq!(eval("[1, [2, [3]]].flat(2).length"), JsValue::Number(3.0));
}
#[test]
fn test_array_flatmap() {
assert_eq!(
eval("[1, 2, 3].flatMap(x => [x, x * 2]).length"),
JsValue::Number(6.0)
);
assert_eq!(
eval("[1, 2, 3].flatMap(x => [x, x * 2])[1]"),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_findlast() {
assert_eq!(
eval("[1, 2, 3, 2].findLast(x => x === 2)"),
JsValue::Number(2.0)
);
assert_eq!(eval("[1, 2, 3].findLast(x => x > 1)"), JsValue::Number(3.0));
assert_eq!(eval("[1, 2, 3].findLast(x => x > 10)"), JsValue::Undefined);
}
#[test]
fn test_array_findlastindex() {
assert_eq!(
eval("[1, 2, 3, 2].findLastIndex(x => x === 2)"),
JsValue::Number(3.0)
);
assert_eq!(
eval("[1, 2, 3].findLastIndex(x => x > 1)"),
JsValue::Number(2.0)
);
assert_eq!(
eval("[1, 2, 3].findLastIndex(x => x > 10)"),
JsValue::Number(-1.0)
);
}
#[test]
fn test_array_toreversed() {
assert_eq!(
eval("let a = [1, 2, 3]; let b = a.toReversed(); b[0]"),
JsValue::Number(3.0)
);
assert_eq!(
eval("let a = [1, 2, 3]; let b = a.toReversed(); a[0]"),
JsValue::Number(1.0)
); }
#[test]
fn test_array_tosorted() {
assert_eq!(
eval("let a = [3, 1, 2]; let b = a.toSorted(); b[0]"),
JsValue::Number(1.0)
);
assert_eq!(
eval("let a = [3, 1, 2]; let b = a.toSorted(); a[0]"),
JsValue::Number(3.0)
); }
#[test]
fn test_array_tospliced() {
assert_eq!(
eval("[1, 2, 3].toSpliced(1, 1, 'a', 'b')[1]"),
JsValue::String(JsString::from("a"))
);
assert_eq!(
eval("[1, 2, 3].toSpliced(1, 1, 'a', 'b').length"),
JsValue::Number(4.0)
);
}
#[test]
fn test_array_with() {
assert_eq!(
eval("[1, 2, 3].with(1, 'x')[1]"),
JsValue::String(JsString::from("x"))
);
assert_eq!(
eval("let a = [1, 2, 3]; let b = a.with(1, 'x'); a[1]"),
JsValue::Number(2.0)
); }
#[test]
fn test_array_keys() {
assert_eq!(
eval("let arr: string[] = ['a', 'b', 'c']; let keys: number[] = arr.keys(); keys[0]"),
JsValue::Number(0.0)
);
assert_eq!(
eval("let arr: string[] = ['a', 'b', 'c']; let keys: number[] = arr.keys(); keys[2]"),
JsValue::Number(2.0)
);
assert_eq!(
eval("([1, 2, 3] as number[]).keys().length"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_values() {
assert_eq!(
eval("let arr: string[] = ['a', 'b', 'c']; let iter = arr.values(); iter.next().value"),
JsValue::String(JsString::from("a"))
);
assert_eq!(
eval("let iter = ([1, 2, 3] as number[]).values(); iter.next(); iter.next().value"),
JsValue::Number(2.0)
);
assert_eq!(
eval("let iter = [42].values(); iter.next(); iter.next().done"),
JsValue::Boolean(true)
);
}
#[test]
fn test_array_entries() {
assert_eq!(
eval("let arr: string[] = ['a', 'b']; let entries = arr.entries(); entries[0][0]"),
JsValue::Number(0.0)
);
assert_eq!(
eval("let arr: string[] = ['a', 'b']; let entries = arr.entries(); entries[0][1]"),
JsValue::String(JsString::from("a"))
);
assert_eq!(
eval("let arr: string[] = ['a', 'b']; let entries = arr.entries(); entries[1][0]"),
JsValue::Number(1.0)
);
assert_eq!(
eval("([1, 2, 3] as number[]).entries().length"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_holes_basic() {
assert_eq!(
eval("const arr: (number | undefined)[] = [1, , 3]; arr[1]"),
JsValue::Undefined
);
}
#[test]
fn test_array_holes_length() {
assert_eq!(
eval("const arr: (number | undefined)[] = [1, , 3]; arr.length"),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_holes_at_start() {
assert_eq!(
eval("const arr: (number | undefined)[] = [, 1, 2]; arr[0]"),
JsValue::Undefined
);
assert_eq!(
eval("const arr: (number | undefined)[] = [, 1, 2]; arr[1]"),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_holes_multiple() {
assert_eq!(
eval("const arr: (number | undefined)[] = [, , 3, , 5]; arr.length"),
JsValue::Number(5.0)
);
assert_eq!(
eval("const arr: (number | undefined)[] = [, , 3, , 5]; arr[2]"),
JsValue::Number(3.0)
);
assert_eq!(
eval("const arr: (number | undefined)[] = [, , 3, , 5]; arr[3]"),
JsValue::Undefined
);
}
#[test]
fn test_array_holes_trailing_comma() {
assert_eq!(
eval("const arr: number[] = [1, 2, ]; arr.length"),
JsValue::Number(2.0)
);
}
#[test]
fn test_reduce_with_object_destructuring() {
assert_eq!(
eval(
r#"
const products = [
{ price: 10, stock: 5 },
{ price: 20, stock: 3 },
];
products.reduce((total, { price, stock }) => total + price * stock, 0)
"#
),
JsValue::Number(110.0)
);
}
#[test]
fn test_reduce_grouping_pattern() {
assert_eq!(
eval(
r#"
const products = [
{ id: 1, category: "X" },
{ id: 2, category: "Y" },
{ id: 3, category: "X" },
];
const grouped = products.reduce((groups, product) => {
const category = product.category;
if (!groups[category]) {
groups[category] = [];
}
groups[category].push(product);
return groups;
}, {});
Object.keys(grouped).length
"#
),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_operations_chain() {
assert_eq!(
eval(
r#"
const data: number[] = [1, 2, 3, 4, 5];
const result = data
.filter(x => x > 2)
.map(x => x * 2)
.reduce((sum, x) => sum + x, 0);
result
"#
),
JsValue::Number(24.0) );
}
#[test]
fn test_quicksort() {
let result = eval(
r#"
function quickSort(arr: number[]): number[] {
if (arr.length <= 1) return arr;
const pivot = arr[Math.floor(arr.length / 2)];
const left = arr.filter((x) => x < pivot);
const middle = arr.filter((x) => x === pivot);
const right = arr.filter((x) => x > pivot);
return [...quickSort(left), ...middle, ...quickSort(right)];
}
quickSort([64, 34, 25, 12]).join(",")
"#,
);
assert_eq!(result, JsValue::String("12,25,34,64".into()));
}
#[test]
fn test_array_spread_sort() {
assert_eq!(
eval(
r#"
const arr: number[] = [3, 1, 2];
const sorted = [...arr].sort((a, b) => a - b);
sorted.join(",")
"#
),
JsValue::String("1,2,3".into())
);
}
#[test]
fn test_debug_array_method_lookup() {
use super::eval_result;
let result = eval_result("const arr = [1, 2, 3]; typeof arr.map");
println!("typeof arr.map: {:?}", result);
let result2 = eval_result("const arr = [1, 2, 3]; typeof arr.push");
println!("typeof arr.push: {:?}", result2);
let result3 = eval_result("[1, 2, 3].map(function(x) { return x * 2; })");
println!("map with function: {:?}", result3);
let result4 = eval_result("[1, 2, 3].map(x => x * 2)");
println!("map with arrow: {:?}", result4);
let result5 = eval_result("const arr = [1, 2, 3]; arr.map");
println!("arr.map value: {:?}", result5);
let result6 = eval_result("const arr = [1, 2, 3]; arr.hasOwnProperty('map')");
println!("arr.hasOwnProperty('map'): {:?}", result6);
let result7 = eval_result("const arr = [1, 2, 3]; arr.hasOwnProperty('push')");
println!("arr.hasOwnProperty('push'): {:?}", result7);
let result8 = eval_result("const arr = [1, 2, 3]; Object.keys(arr).length");
println!("Object.keys(arr).length: {:?}", result8);
let result9 = eval_result("const arr = [1, 2, 3]; const m = arr.map; typeof m");
println!("stored method typeof: {:?}", result9);
let result10 = eval_result("const arr = [1, 2, 3]; const m = arr.map; m.call(arr, x => x * 2)");
println!("m.call(arr, fn): {:?}", result10);
let result11 = eval_result("const arr = [1, 2, 3]; Object.getPrototypeOf(arr) !== null");
println!("has prototype: {:?}", result11);
let result12 = eval_result("const arr = [1, 2, 3]; arr.push(4)");
println!("arr.push(4): {:?}", result12);
let result13 = eval_result("[1, 2, 3].push(4)");
println!("[1, 2, 3].push(4): {:?}", result13);
let result14 = eval_result("[1, 2, 3].push");
println!("[1, 2, 3].push: {:?}", result14);
let result15 = eval_result("const arr = [1]; arr.map(function(x) { return x; })");
println!("arr.map with simplest callback: {:?}", result15);
assert!(
result.is_ok() || result.is_err(),
"This test is for debugging"
);
}
#[test]
fn test_array_map_on_array_like() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: number; 1: number } = { length: 2, 0: 10, 1: 20 };
const result: number[] = Array.prototype.map.call(obj, function(x: number): number { return x * 2; });
result[0]
"#
),
JsValue::Number(20.0)
);
}
#[test]
fn test_array_map_on_array_like_result_length() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: number; 1: number } = { length: 2, 0: 10, 1: 20 };
const result: number[] = Array.prototype.map.call(obj, function(x: number): number { return x * 2; });
result.length
"#
),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_filter_on_array_like() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: number; 1: number; 2: number } = { length: 3, 0: 1, 1: 2, 2: 3 };
const result: number[] = Array.prototype.filter.call(obj, function(x: number): boolean { return x > 1; });
result.length
"#
),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_foreach_on_array_like() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: number; 1: number } = { length: 2, 0: 10, 1: 20 };
let sum: number = 0;
Array.prototype.forEach.call(obj, function(x: number): void { sum += x; });
sum
"#
),
JsValue::Number(30.0)
);
}
#[test]
fn test_array_reduce_on_array_like() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: number; 1: number; 2: number } = { length: 3, 0: 1, 1: 2, 2: 3 };
Array.prototype.reduce.call(obj, function(acc: number, x: number): number { return acc + x; }, 0)
"#
),
JsValue::Number(6.0)
);
}
#[test]
fn test_array_some_on_array_like() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: number; 1: number } = { length: 2, 0: 1, 1: 5 };
Array.prototype.some.call(obj, function(x: number): boolean { return x > 3; })
"#
),
JsValue::Boolean(true)
);
}
#[test]
fn test_array_every_on_array_like() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: number; 1: number } = { length: 2, 0: 1, 1: 2 };
Array.prototype.every.call(obj, function(x: number): boolean { return x > 0; })
"#
),
JsValue::Boolean(true)
);
}
#[test]
fn test_array_find_on_array_like() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: number; 1: number; 2: number } = { length: 3, 0: 1, 1: 5, 2: 10 };
Array.prototype.find.call(obj, function(x: number): boolean { return x > 3; })
"#
),
JsValue::Number(5.0)
);
}
#[test]
fn test_array_indexof_on_array_like() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: string; 1: string; 2: string } = { length: 3, 0: "a", 1: "b", 2: "c" };
Array.prototype.indexOf.call(obj, "b")
"#
),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_includes_on_array_like() {
assert_eq!(
eval(
r#"
const obj: { length: number; 0: number; 1: number } = { length: 2, 0: 10, 1: 20 };
Array.prototype.includes.call(obj, 20)
"#
),
JsValue::Boolean(true)
);
}
#[test]
fn test_array_map_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
function callbackfn(val: number, idx: number, obj: any): boolean {
return val < 10;
}
const obj: any = {
0: 11,
1: 9,
length: {
toString: function(): string {
return '2';
}
}
};
const newArr: boolean[] = Array.prototype.map.call(obj, callbackfn);
newArr.length
"#
),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_map_on_array_like_with_valueof_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 'a',
1: 'b',
2: 'c',
length: {
valueOf: function(): number {
return 3;
}
}
};
const newArr: string[] = Array.prototype.map.call(obj, (x: string) => x.toUpperCase());
newArr.length
"#
),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_map_on_array_like_valueof_result() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 'a',
1: 'b',
length: { valueOf: () => 2 }
};
const arr: string[] = Array.prototype.map.call(obj, (x: string) => x.toUpperCase());
arr[0] + arr[1]
"#
),
JsValue::String(JsString::from("AB"))
);
}
#[test]
fn test_array_filter_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 1,
1: 2,
2: 3,
3: 4,
length: { toString: () => '4' }
};
const arr: number[] = Array.prototype.filter.call(obj, (x: number) => x > 2);
arr.length
"#
),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_filter_on_array_like_result_values() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 1,
1: 2,
2: 3,
length: { valueOf: () => 3 }
};
const arr: number[] = Array.prototype.filter.call(obj, (x: number) => x >= 2);
arr[0] + arr[1]
"#
),
JsValue::Number(5.0) );
}
#[test]
fn test_array_foreach_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 10,
1: 20,
2: 30,
length: { toString: () => '3' }
};
let sum: number = 0;
Array.prototype.forEach.call(obj, (x: number) => { sum = sum + x; });
sum
"#
),
JsValue::Number(60.0)
);
}
#[test]
fn test_array_reduce_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 1,
1: 2,
2: 3,
length: { valueOf: () => 3 }
};
Array.prototype.reduce.call(obj, (acc: number, x: number) => acc + x, 0)
"#
),
JsValue::Number(6.0)
);
}
#[test]
fn test_array_every_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 2,
1: 4,
2: 6,
length: { toString: () => '3' }
};
Array.prototype.every.call(obj, (x: number) => x % 2 === 0)
"#
),
JsValue::Boolean(true)
);
}
#[test]
fn test_array_some_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 1,
1: 3,
2: 5,
length: { valueOf: () => 3 }
};
Array.prototype.some.call(obj, (x: number) => x > 4)
"#
),
JsValue::Boolean(true)
);
}
#[test]
fn test_array_find_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 'apple',
1: 'banana',
2: 'cherry',
length: { toString: () => '3' }
};
Array.prototype.find.call(obj, (x: string) => x.startsWith('b'))
"#
),
JsValue::String(JsString::from("banana"))
);
}
#[test]
fn test_array_findindex_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 10,
1: 20,
2: 30,
length: { valueOf: () => 3 }
};
Array.prototype.findIndex.call(obj, (x: number) => x === 20)
"#
),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_indexof_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 'x',
1: 'y',
2: 'z',
length: { toString: () => '3' }
};
Array.prototype.indexOf.call(obj, 'y')
"#
),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_includes_on_array_like_with_object_length() {
assert_eq!(
eval(
r#"
const obj: any = {
0: 100,
1: 200,
length: { valueOf: () => 2 }
};
Array.prototype.includes.call(obj, 200)
"#
),
JsValue::Boolean(true)
);
}
#[test]
fn test_array_length_coercion_boolean_true() {
assert_eq!(
eval(
r#"
const obj: any = { 0: 'first', length: true };
Array.prototype.map.call(obj, (x: string) => x).length
"#
),
JsValue::Number(1.0)
);
}
#[test]
fn test_array_length_coercion_boolean_false() {
assert_eq!(
eval(
r#"
const obj: any = { 0: 'first', length: false };
Array.prototype.map.call(obj, (x: string) => x).length
"#
),
JsValue::Number(0.0)
);
}
#[test]
fn test_array_length_coercion_null() {
assert_eq!(
eval(
r#"
const obj: any = { 0: 'first', length: null };
Array.prototype.map.call(obj, (x: string) => x).length
"#
),
JsValue::Number(0.0)
);
}
#[test]
fn test_array_length_coercion_undefined() {
assert_eq!(
eval(
r#"
const obj: any = { 0: 'first', length: undefined };
Array.prototype.map.call(obj, (x: string) => x).length
"#
),
JsValue::Number(0.0)
);
}
#[test]
fn test_array_length_coercion_string_number() {
assert_eq!(
eval(
r#"
const obj: any = { 0: 'a', 1: 'b', 2: 'c', length: '3' };
Array.prototype.map.call(obj, (x: string) => x).length
"#
),
JsValue::Number(3.0)
);
}
#[test]
fn test_array_length_coercion_float_truncated() {
assert_eq!(
eval(
r#"
const obj: any = { 0: 'a', 1: 'b', 2: 'c', length: 2.9 };
Array.prototype.map.call(obj, (x: string) => x).length
"#
),
JsValue::Number(2.0)
);
}
#[test]
fn test_array_length_coercion_negative() {
assert_eq!(
eval(
r#"
const obj: any = { 0: 'a', length: -5 };
Array.prototype.map.call(obj, (x: string) => x).length
"#
),
JsValue::Number(0.0)
);
}