use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction};
use indoc::indoc;
#[test]
fn typeof_string() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
const a = "hello";
typeof a;
"#},
"string",
)]);
}
#[test]
fn typeof_number() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
let a = 1234;
typeof a;
"#},
"number",
)]);
}
#[test]
fn basic_op() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
const a = 1;
const b = 2;
a + b
"#},
3,
)]);
}
#[test]
fn try_catch_finally_from_init() {
run_test_actions([TestAction::assert_opaque_error(
indoc! {r#"
try {
[(() => {throw "h";})()];
} catch (x) {
throw "h";
} finally {
}
"#},
"h",
)]);
}
#[test]
fn multiple_catches() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
try {
try {
[(() => {throw "h";})()];
} catch (x) {
throw "h";
}
} catch (y) {
}
"#},
JsValue::undefined(),
)]);
}
#[test]
fn use_last_expr_try_block() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
try {
19;
7.5;
"Hello!";
} catch (y) {
14;
"Bye!"
}
"#},
"Hello!",
)]);
}
#[test]
fn use_last_expr_catch_block() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
try {
throw Error("generic error");
19;
7.5;
} catch (y) {
14;
"Hello!";
}
"#},
"Hello!",
)]);
}
#[test]
fn no_use_last_expr_finally_block() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
try {
} catch (y) {
} finally {
"Unused";
}
"#},
JsValue::undefined(),
)]);
}
#[test]
fn finally_block_binding_env() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
let buf = "Hey hey";
try {
} catch (y) {
} finally {
let x = " people";
buf += x;
}
buf
"#},
"Hey hey people",
)]);
}
#[test]
fn run_super_method_in_object() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
let proto = {
m() { return "super"; }
};
let obj = {
v() { return super.m(); }
};
Object.setPrototypeOf(obj, proto);
obj.v();
"#},
"super",
)]);
}
#[test]
fn get_reference_by_super() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
var fromA, fromB;
var A = { fromA: 'a', fromB: 'a' };
var B = { fromB: 'b' };
Object.setPrototypeOf(B, A);
var obj = {
fromA: 'c',
fromB: 'c',
method() {
fromA = (() => { return super.fromA; })();
fromB = (() => { return super.fromB; })();
}
};
Object.setPrototypeOf(obj, B);
obj.method();
fromA + fromB
"#},
"ab",
)]);
}
#[test]
fn super_call_constructor_null() {
run_test_actions([TestAction::assert_native_error(
indoc! {r#"
class A extends Object {
constructor() {
Object.setPrototypeOf(A, null);
super(A);
}
}
new A();
"#},
JsNativeErrorKind::Type,
"super constructor object must be constructor",
)]);
}
#[test]
fn super_call_get_constructor_before_arguments_execution() {
run_test_actions([TestAction::assert(indoc! {r#"
class A extends Object {
constructor() {
super(Object.setPrototypeOf(A, null));
}
}
new A() instanceof A;
"#})]);
}
#[test]
fn order_of_execution_in_assigment() {
run_test_actions([
TestAction::run(indoc! {r#"
let i = 0;
let array = [[]];
array[i++][i++] = i++;
"#}),
TestAction::assert_eq("i", 3),
TestAction::assert_eq("array.length", 1),
TestAction::assert_eq("array[0].length", 2),
]);
}
#[test]
fn order_of_execution_in_assigment_with_comma_expressions() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
let result = "";
function f(i) {
result += i;
}
let a = [[]];
(f(1), a)[(f(2), 0)][(f(3), 0)] = (f(4), 123);
result
"#},
"1234",
)]);
}
#[test]
fn loop_runtime_limit() {
run_test_actions([
TestAction::assert_eq(
indoc! {r#"
for (let i = 0; i < 20; ++i) { }
"#},
JsValue::undefined(),
),
TestAction::inspect_context(|context| {
context.runtime_limits_mut().set_loop_iteration_limit(10);
}),
TestAction::assert_native_error(
indoc! {r#"
for (let i = 0; i < 20; ++i) { }
"#},
JsNativeErrorKind::RuntimeLimit,
"Maximum loop iteration limit 10 exceeded",
),
TestAction::assert_eq(
indoc! {r#"
for (let i = 0; i < 10; ++i) { }
"#},
JsValue::undefined(),
),
TestAction::assert_native_error(
indoc! {r#"
while (1) { }
"#},
JsNativeErrorKind::RuntimeLimit,
"Maximum loop iteration limit 10 exceeded",
),
]);
}
#[test]
fn recursion_runtime_limit() {
run_test_actions([
TestAction::run(indoc! {r#"
function factorial(n) {
if (n == 0) {
return 1;
}
return n * factorial(n - 1);
}
"#}),
TestAction::assert_eq("factorial(8)", JsValue::new(40_320)),
TestAction::assert_eq("factorial(11)", JsValue::new(39_916_800)),
TestAction::inspect_context(|context| {
context.runtime_limits_mut().set_recursion_limit(10);
}),
TestAction::assert_native_error(
"factorial(11)",
JsNativeErrorKind::RuntimeLimit,
"Maximum recursion limit 10 exceeded",
),
TestAction::assert_eq("factorial(8)", JsValue::new(40_320)),
TestAction::assert_native_error(
indoc! {r#"
function x() {
x()
}
x()
"#},
JsNativeErrorKind::RuntimeLimit,
"Maximum recursion limit 10 exceeded",
),
]);
}
#[test]
fn arguments_object_constructor_valid_index() {
run_test_actions([TestAction::assert_eq(
indoc! {r#"
let args;
function F(a = 1) {
args = arguments;
}
new F();
typeof args
"#},
"object",
)]);
}
#[test]
fn empty_return_values() {
run_test_actions([
TestAction::run(indoc! {r#"do {{}} while (false);"#}),
TestAction::run(indoc! {r#"do try {{}} catch {} while (false);"#}),
TestAction::run(indoc! {r#"do {} while (false);"#}),
TestAction::run(indoc! {r#"do try {{}{}} catch {} while (false);"#}),
TestAction::run(indoc! {r#"do {{}{}} while (false);"#}),
TestAction::run(indoc! {r#"do {;{}} while (false);"#}),
TestAction::run(indoc! {r#"do {e: {}} while (false);"#}),
TestAction::run(indoc! {r#"do {e: ;} while (false);"#}),
TestAction::run(indoc! {r#"do { break } while (false);"#}),
TestAction::run(indoc! {r#"while (true) a: break"#}),
TestAction::run(indoc! {r#"while (true) a: {"a"; break};"#}),
TestAction::run(indoc! {r#"do {"a";{}} while (false);"#}),
TestAction::run(indoc! {r#"
switch (false) {
default: {}
}
"#}),
TestAction::run(indoc! {r#"
switch (false) {
default: {}{}
}
"#}),
TestAction::run(indoc! {r#"
switch (false) {
default: ;{}{}
}
"#}),
]);
}