Skip to content

Commit 15a6b94

Browse files
authored
test: add Number.toPrecision and operator edge case tests (#4997)
<!--- Thank you for contributing to Boa! Please fill out the template below, and remove or add any information as you feel necessary. ---> It changes the following: - fixed typo in `LinearSpan` doc comment (`LinearPosition` -> `LinearSpan`) - Added `toPrecision` edge case tests for `NaN`, `Infinity`, `-0`, and carry over rounding - Added tests for `NaN` comparisons, `BigInt` mixed type throwing behaviors, `BigInt` unsigned right shift (`>>>`) throws and `instanceof` with custom `[Symbol.hasInstance]`
1 parent 8225dc5 commit 15a6b94

File tree

3 files changed

+186
-1
lines changed

3 files changed

+186
-1
lines changed

core/ast/src/position.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ pub struct LinearSpan {
261261
end: LinearPosition,
262262
}
263263
impl LinearSpan {
264-
/// Creates a new `LinearPosition`.
264+
/// Creates a new `LinearSpan`.
265265
///
266266
/// # Panics
267267
///

core/engine/src/builtins/number/tests.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,3 +642,29 @@ fn issue_2717() {
642642
),
643643
]);
644644
}
645+
646+
#[test]
647+
fn to_precision_edge_cases() {
648+
run_test_actions([
649+
TestAction::assert_eq("(NaN).toPrecision(3)", js_str!("NaN")),
650+
TestAction::assert_eq("(Infinity).toPrecision(3)", js_str!("Infinity")),
651+
TestAction::assert_eq("(-Infinity).toPrecision(3)", js_str!("-Infinity")),
652+
TestAction::assert_eq("(-0).toPrecision(5)", js_str!("0.0000")),
653+
// Carry-over rounding tests
654+
TestAction::assert_eq("(9.95).toPrecision(2)", js_str!("9.9")),
655+
TestAction::assert_eq("(99.95).toPrecision(3)", js_str!("100")),
656+
TestAction::assert_eq("(999.95).toPrecision(4)", js_str!("1000")),
657+
TestAction::assert_eq("(9.5).toPrecision(1)", js_str!("1e+1")),
658+
TestAction::assert_eq("(123.456).toPrecision(5)", js_str!("123.46")),
659+
TestAction::assert_eq("(0.00456).toPrecision(3)", js_str!("0.00456")),
660+
TestAction::assert_eq("(0.0000001).toPrecision(2)", js_str!("1.0e-7")),
661+
TestAction::assert_eq("(0.000000123).toPrecision(3)", js_str!("1.23e-7")),
662+
TestAction::assert_eq("(123456789).toPrecision(3)", js_str!("1.23e+8")),
663+
TestAction::assert_eq("(0.1 + 0.2).toPrecision(1)", js_str!("0.3")),
664+
TestAction::assert_eq("(123).toPrecision(3)", js_str!("123")),
665+
TestAction::assert_eq("(123).toPrecision(4)", js_str!("123.0")),
666+
TestAction::assert_eq("(0).toPrecision(1)", js_str!("0")),
667+
TestAction::assert_eq("(1).toPrecision(1)", js_str!("1")),
668+
TestAction::assert_eq("(9).toPrecision(1)", js_str!("9")),
669+
]);
670+
}

core/engine/src/tests/operators.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,3 +791,162 @@ mod in_operator {
791791
)]);
792792
}
793793
}
794+
795+
/// `NaN` comparison, both via numeric literals (fast path) and
796+
/// via `Number` object properties (slow path through `abstract_relation`).
797+
///
798+
#[test]
799+
fn nan_comparisons() {
800+
run_test_actions([
801+
TestAction::assert("!(NaN == NaN)"),
802+
TestAction::assert("NaN != NaN"),
803+
TestAction::assert("!(NaN === NaN)"),
804+
TestAction::assert("NaN !== NaN"),
805+
TestAction::assert("!(NaN < 1)"),
806+
TestAction::assert("!(NaN > 1)"),
807+
TestAction::assert("!(NaN <= 1)"),
808+
TestAction::assert("!(NaN >= 1)"),
809+
TestAction::assert("!(1 < NaN)"),
810+
TestAction::assert("!(1 > NaN)"),
811+
TestAction::assert("!(1 <= NaN)"),
812+
TestAction::assert("!(1 >= NaN)"),
813+
TestAction::assert("!(NaN < NaN)"),
814+
TestAction::assert("!(NaN > NaN)"),
815+
TestAction::assert("!(NaN <= NaN)"),
816+
TestAction::assert("!(NaN >= NaN)"),
817+
TestAction::assert("!(NaN < Infinity)"),
818+
TestAction::assert("!(NaN > -Infinity)"),
819+
TestAction::assert("!(NaN < -Infinity)"),
820+
TestAction::assert("!(NaN > Infinity)"),
821+
TestAction::assert("!(new Number(NaN) < 1)"),
822+
TestAction::assert("!(new Number(NaN) > 1)"),
823+
TestAction::assert("!(new Number(NaN) <= 1)"),
824+
TestAction::assert("!(new Number(NaN) >= 1)"),
825+
TestAction::assert("!(1 < new Number(NaN))"),
826+
TestAction::assert("!(1 > new Number(NaN))"),
827+
TestAction::assert("!(1 <= new Number(NaN))"),
828+
TestAction::assert("!(1 >= new Number(NaN))"),
829+
TestAction::assert("Number.isNaN(NaN)"),
830+
TestAction::assert("Number.isNaN(0/0)"),
831+
TestAction::assert("!Number.isNaN(undefined)"),
832+
TestAction::assert("!Number.isNaN('NaN')"),
833+
]);
834+
}
835+
836+
#[test]
837+
fn bigint_mixed_type_operations() {
838+
const MIXED_MSG: &str = "cannot mix BigInt and other types, use explicit conversions";
839+
run_test_actions([
840+
TestAction::assert_native_error("1n + 1", JsNativeErrorKind::Type, MIXED_MSG),
841+
TestAction::assert_native_error("1n - 1", JsNativeErrorKind::Type, MIXED_MSG),
842+
TestAction::assert_native_error("1n * 2", JsNativeErrorKind::Type, MIXED_MSG),
843+
TestAction::assert_native_error("4n / 2", JsNativeErrorKind::Type, MIXED_MSG),
844+
TestAction::assert_native_error("5n % 2", JsNativeErrorKind::Type, MIXED_MSG),
845+
TestAction::assert_native_error("2n ** 3", JsNativeErrorKind::Type, MIXED_MSG),
846+
TestAction::assert_native_error("1 + 1n", JsNativeErrorKind::Type, MIXED_MSG),
847+
TestAction::assert_eq("'hello' + 1n", js_str!("hello1")),
848+
TestAction::assert_eq("1n + 'hello'", js_str!("1hello")),
849+
TestAction::assert_native_error(
850+
indoc! {r#"
851+
let numObj = { valueOf() { return 42; }, toString() { return "42"; } };
852+
1n + numObj
853+
"#},
854+
JsNativeErrorKind::Type,
855+
MIXED_MSG,
856+
),
857+
TestAction::assert("1n < 2"),
858+
TestAction::assert("!(1n > 2)"),
859+
TestAction::assert("1n <= 1"),
860+
TestAction::assert("2n >= 2"),
861+
TestAction::assert("(2n + 3n) === 5n"),
862+
TestAction::assert("(10n - 4n) === 6n"),
863+
TestAction::assert("(3n * 4n) === 12n"),
864+
TestAction::assert("(10n / 3n) === 3n"),
865+
TestAction::assert("(10n % 3n) === 1n"),
866+
TestAction::assert("(2n ** 10n) === 1024n"),
867+
]);
868+
}
869+
870+
#[test]
871+
fn ushr_bigint_type_error() {
872+
run_test_actions([
873+
TestAction::assert_native_error(
874+
"1n >>> 0n",
875+
JsNativeErrorKind::Type,
876+
"BigInts have no unsigned right shift, use >> instead",
877+
),
878+
TestAction::assert_native_error(
879+
"255n >>> 4n",
880+
JsNativeErrorKind::Type,
881+
"BigInts have no unsigned right shift, use >> instead",
882+
),
883+
TestAction::assert("(8n >> 2n) === 2n"),
884+
TestAction::assert("(-1n >> 63n) === -1n"),
885+
TestAction::assert("(1n << 4n) === 16n"),
886+
]);
887+
}
888+
889+
#[test]
890+
fn instanceof_custom_has_instance() {
891+
run_test_actions([
892+
TestAction::assert(indoc! {r#"
893+
class AlwaysTrue {
894+
static [Symbol.hasInstance](_instance) {
895+
return true;
896+
}
897+
}
898+
({} instanceof AlwaysTrue)
899+
"#}),
900+
TestAction::assert(indoc! {r#"
901+
class AlwaysFalse {
902+
static [Symbol.hasInstance](_instance) {
903+
return false;
904+
}
905+
}
906+
let obj = new AlwaysFalse();
907+
!(obj instanceof AlwaysFalse)
908+
"#}),
909+
TestAction::assert(indoc! {r#"
910+
const EvenNumber = {
911+
[Symbol.hasInstance](value) {
912+
return typeof value === 'number' && value % 2 === 0;
913+
}
914+
};
915+
(2 instanceof EvenNumber) && !(3 instanceof EvenNumber)
916+
"#}),
917+
TestAction::assert_native_error(
918+
indoc! {r#"
919+
class Throws {
920+
static [Symbol.hasInstance](_instance) {
921+
throw new TypeError("hasInstance threw");
922+
}
923+
}
924+
({} instanceof Throws)
925+
"#},
926+
JsNativeErrorKind::Type,
927+
"hasInstance threw",
928+
),
929+
TestAction::assert_native_error(
930+
"let s = new String(); s instanceof {}",
931+
JsNativeErrorKind::Type,
932+
"right-hand side of 'instanceof' is not callable",
933+
),
934+
TestAction::assert_native_error(
935+
"42 instanceof 'not-a-constructor'",
936+
JsNativeErrorKind::Type,
937+
"right-hand side of 'instanceof' should be an object, got `string`",
938+
),
939+
TestAction::assert(indoc! {r#"
940+
const Truthy = {
941+
[Symbol.hasInstance]() { return 1; }
942+
};
943+
({} instanceof Truthy)
944+
"#}),
945+
TestAction::assert(indoc! {r#"
946+
const Falsy = {
947+
[Symbol.hasInstance]() { return 0; }
948+
};
949+
!({} instanceof Falsy)
950+
"#}),
951+
]);
952+
}

0 commit comments

Comments
 (0)