use pipa::{JSRuntime, eval};
fn js_eq(code: &str) {
let mut rt = JSRuntime::new();
let mut ctx = rt.new_context();
eval(&mut ctx, code).unwrap_or_else(|e| panic!("JS error: {}", e));
}
#[test]
fn json_parse_null() {
js_eq("if (JSON.parse('null') !== null) throw new Error('fail');");
}
#[test]
fn json_parse_true() {
js_eq("if (JSON.parse('true') !== true) throw new Error('fail');");
}
#[test]
fn json_parse_false() {
js_eq("if (JSON.parse('false') !== false) throw new Error('fail');");
}
#[test]
fn json_parse_integer() {
js_eq("if (JSON.parse('42') !== 42) throw new Error('fail');");
}
#[test]
fn json_parse_negative_integer() {
js_eq("if (JSON.parse('-7') !== -7) throw new Error('fail');");
}
#[test]
fn json_parse_float() {
js_eq("if (JSON.parse('3.14') !== 3.14) throw new Error('fail');");
}
#[test]
fn json_parse_exponent() {
js_eq("if (JSON.parse('1e2') !== 100) throw new Error('fail');");
js_eq("if (JSON.parse('1E+3') !== 1000) throw new Error('fail');");
js_eq("if (JSON.parse('2.5e-1') !== 0.25) throw new Error('fail');");
}
#[test]
fn json_parse_string() {
js_eq("if (JSON.parse('\"hello\"') !== 'hello') throw new Error('fail');");
}
#[test]
fn json_parse_empty_string() {
js_eq("if (JSON.parse('\"\"') !== '') throw new Error('fail');");
}
#[test]
fn json_parse_string_escapes() {
js_eq("if (JSON.parse('\"\\\\n\"') !== '\\n') throw new Error('newline');");
js_eq("if (JSON.parse('\"\\\\t\"') !== '\\t') throw new Error('tab');");
js_eq("if (JSON.parse('\"\\\\\\\\\"') !== '\\\\') throw new Error('backslash');");
js_eq("if (JSON.parse('\"\\\\\"\"') !== '\"') throw new Error('quote');");
js_eq("if (JSON.parse('\"\\\\/\"') !== '/') throw new Error('slash');");
js_eq("if (JSON.parse('\"\\\\r\"') !== '\\r') throw new Error('cr');");
js_eq("if (JSON.parse('\"\\\\b\"').charCodeAt(0) !== 8) throw new Error('bs');");
js_eq("if (JSON.parse('\"\\\\f\"').charCodeAt(0) !== 12) throw new Error('ff');");
}
#[test]
fn json_parse_string_unicode_escape() {
js_eq("if (JSON.parse('\"\\\\u0041\"') !== 'A') throw new Error('fail');");
}
#[test]
fn json_parse_empty_array() {
js_eq("if (JSON.parse('[]').length !== 0) throw new Error('fail');");
}
#[test]
fn json_parse_simple_array() {
js_eq(
"var a = JSON.parse('[1,2,3]'); \
if (a.length !== 3 || a[0] !== 1 || a[2] !== 3) throw new Error('fail');",
);
}
#[test]
fn json_parse_mixed_array() {
js_eq(
"var a = JSON.parse('[1,\"two\",true,null]'); \
if (a.length !== 4 || a[0] !== 1 || a[1] !== 'two' || a[2] !== true || a[3] !== null) throw new Error('fail');",
);
}
#[test]
fn json_parse_nested_array() {
js_eq(
"var a = JSON.parse('[[1,2],[3,4]]'); \
if (a.length !== 2 || a[0][0] !== 1 || a[1][1] !== 4) throw new Error('fail');",
);
}
#[test]
fn json_parse_empty_object() {
js_eq("var o = JSON.parse('{}'); if (typeof o !== 'object') throw new Error('fail');");
}
#[test]
fn json_parse_simple_object() {
js_eq(
"var o = JSON.parse('{\"a\":1,\"b\":2}'); \
if (o.a !== 1 || o.b !== 2) throw new Error('fail');",
);
}
#[test]
fn json_parse_nested_object() {
js_eq(
"var o = JSON.parse('{\"outer\":{\"inner\":42}}'); \
if (o.outer.inner !== 42) throw new Error('fail');",
);
}
#[test]
fn json_parse_complex() {
js_eq(
"var o = JSON.parse('{\"name\":\"test\",\"values\":[1,2,3],\"nested\":{\"key\":\"val\"}}'); \
if (o.name !== 'test') throw new Error('name'); \
if (o.values.length !== 3) throw new Error('values'); \
if (o.nested.key !== 'val') throw new Error('nested');",
);
}
#[test]
fn json_parse_whitespace() {
js_eq("if (JSON.parse(' 42 ') !== 42) throw new Error('fail');");
js_eq("var a = JSON.parse(' [ 1 , 2 ] '); if (a.length !== 2) throw new Error('fail');");
}
#[test]
fn json_parse_invalid_returns_undefined() {
let mut rt = JSRuntime::new();
let mut ctx = rt.new_context();
let r = eval(&mut ctx, "JSON.parse('invalid')").unwrap();
assert!(r.is_undefined(), "expected undefined for invalid JSON");
}
#[test]
fn json_stringify_null() {
js_eq("if (JSON.stringify(null) !== 'null') throw new Error('fail');");
}
#[test]
fn json_stringify_bool() {
js_eq("if (JSON.stringify(true) !== 'true') throw new Error('fail');");
js_eq("if (JSON.stringify(false) !== 'false') throw new Error('fail');");
}
#[test]
fn json_stringify_number() {
js_eq("if (JSON.stringify(42) !== '42') throw new Error('fail');");
js_eq("if (JSON.stringify(3.14) !== '3.14') throw new Error('fail');");
}
#[test]
fn json_stringify_nan_null() {
js_eq("if (JSON.stringify(NaN) !== 'null') throw new Error('fail');");
js_eq("if (JSON.stringify(Infinity) !== 'null') throw new Error('fail');");
}
#[test]
fn json_stringify_string() {
js_eq("if (JSON.stringify('hello') !== '\"hello\"') throw new Error('fail');");
}
#[test]
fn json_stringify_string_escapes() {
js_eq("if (JSON.stringify('a\\nb') !== '\"a\\\\nb\"') throw new Error('fail');");
}
#[test]
fn json_stringify_undefined() {
js_eq("if (JSON.stringify(undefined) !== 'null') throw new Error('fail');");
}
#[test]
fn json_stringify_empty_array() {
js_eq("if (JSON.stringify([]) !== '[]') throw new Error('fail');");
}
#[test]
fn json_stringify_array() {
js_eq("if (JSON.stringify([1,2,3]) !== '[1,2,3]') throw new Error('fail');");
}
#[test]
fn json_stringify_array_with_null() {
js_eq("if (JSON.stringify([1,null,3]) !== '[1,null,3]') throw new Error('fail');");
}
#[test]
fn json_stringify_empty_object() {
js_eq("if (JSON.stringify({}) !== '{}') throw new Error('fail');");
}
#[test]
fn json_stringify_simple_object() {
js_eq("if (JSON.stringify({a:1}) !== '{\"a\":1}') throw new Error('fail');");
}
#[test]
fn json_stringify_nested() {
js_eq(
"var s = JSON.stringify({a:{b:1}}); \
if (s !== '{\"a\":{\"b\":1}}') throw new Error('got: ' + s);",
);
}
#[test]
fn json_stringify_skips_undefined() {
js_eq(
"var s = JSON.stringify({a:1,b:undefined}); \
if (s !== '{\"a\":1}') throw new Error('got: ' + s);",
);
}
#[test]
fn json_stringify_skips_functions() {
js_eq(
"var s = JSON.stringify({a:1,fn:function(){}}); \
if (s !== '{\"a\":1}') throw new Error('got: ' + s);",
);
}
#[test]
fn json_stringify_circular() {
js_eq(
"var obj = {a:1}; obj.self = obj; \
if (JSON.stringify(obj) !== '{\"a\":1,\"self\":null}') throw new Error('fail');",
);
}
#[test]
fn json_stringify_space_number() {
js_eq(
"var s = JSON.stringify({a:1}, null, 2); \
if (s.indexOf('\\n') < 0) throw new Error('expected newlines: ' + s);",
);
}
#[test]
fn json_stringify_space_string() {
js_eq(
"var s = JSON.stringify([1], null, '\\t'); \
if (s.indexOf('\\t') < 0) throw new Error('expected tabs: ' + s);",
);
}
#[test]
fn json_roundtrip_object() {
js_eq(
"var obj = {x:1, y:'hello', z:true, w:null}; \
var s = JSON.stringify(obj); \
var parsed = JSON.parse(s); \
if (parsed.x !== 1 || parsed.y !== 'hello' || parsed.z !== true || parsed.w !== null) throw new Error('roundtrip fail');",
);
}
#[test]
fn json_roundtrip_nested() {
js_eq(
"var obj = {arr:[1,2,3], obj:{nested:true}}; \
var s = JSON.stringify(obj); \
var parsed = JSON.parse(s); \
if (parsed.arr.length !== 3 || parsed.obj.nested !== true) throw new Error('roundtrip fail');",
);
}
#[test]
fn json_roundtrip_large_number() {
js_eq(
"var n = JSON.parse('9999999999'); \
if (n !== 9999999999) throw new Error('got: ' + n);",
);
}