1#[cfg(test)]
2mod tests {
3 use crate::shared::structures::functions::FunctionMap;
4 use crate::shared::structures::state_manager::StateManager;
5 use crate::shared::utils::ast::convertors::{
6 create_bool_expr, create_null_expr, create_number_expr, create_string_expr,
7 };
8 use crate::shared::utils::js::evaluate::evaluate;
9 use stylex_ast::ast::factories::create_ident;
10 use stylex_structures::stylex_options::StyleXOptions;
11 use swc_core::common::util::take::Take;
12 use swc_core::{
13 common::{DUMMY_SP, GLOBALS, Globals},
14 ecma::ast::{
15 ArrayLit, AwaitExpr, BinExpr, BinaryOp, CallExpr, Callee, ComputedPropName, Expr,
16 ExprOrSpread, IdentName, KeyValueProp, MemberExpr, MemberProp, ObjectLit, OptChainBase,
17 OptChainExpr, Prop, PropName, PropOrSpread, Regex, UnaryExpr, UnaryOp,
18 },
19 };
20
21 fn make_undefined_expr() -> Expr {
25 Expr::Ident(create_ident("undefined"))
26 }
27
28 fn make_ident_expr(name: &str) -> Expr {
30 Expr::Ident(create_ident(name))
31 }
32
33 fn make_member_expr(obj: Expr, prop: &str) -> Expr {
35 Expr::Member(MemberExpr {
36 span: DUMMY_SP,
37 obj: Box::new(obj),
38 prop: MemberProp::Ident(IdentName::new(prop.into(), DUMMY_SP)),
39 })
40 }
41
42 fn make_optional_member_expr(obj: Expr, prop: &str) -> Expr {
44 Expr::OptChain(OptChainExpr {
45 span: DUMMY_SP,
46 optional: true,
47 base: Box::new(OptChainBase::Member(MemberExpr {
48 span: DUMMY_SP,
49 obj: Box::new(obj),
50 prop: MemberProp::Ident(IdentName::new(prop.into(), DUMMY_SP)),
51 })),
52 })
53 }
54
55 fn make_unary_expr(op: UnaryOp, arg: Expr) -> Expr {
57 Expr::Unary(UnaryExpr {
58 span: DUMMY_SP,
59 op,
60 arg: Box::new(arg),
61 })
62 }
63
64 fn make_binary_expr(left: Expr, op: BinaryOp, right: Expr) -> Expr {
66 Expr::Bin(BinExpr {
67 span: DUMMY_SP,
68 left: Box::new(left),
69 op,
70 right: Box::new(right),
71 })
72 }
73
74 fn make_await_expr(arg: Expr) -> Expr {
76 Expr::Await(AwaitExpr {
77 span: DUMMY_SP,
78 arg: Box::new(arg),
79 })
80 }
81
82 fn evaluate_expr(expr: &Expr) -> (bool, bool) {
83 let mut state_manager = StateManager::new(StyleXOptions::default());
84 let fns = FunctionMap::default();
85 let result = evaluate(expr, &mut state_manager, &fns);
86 (result.confident, result.value.is_some())
87 }
88
89 #[test]
92 fn test_optional_chaining_with_null_returns_none() {
93 let opt_chain = make_optional_member_expr(create_null_expr(), "prop");
94 let (_confident, has_value) = evaluate_expr(&opt_chain);
95 assert!(!has_value, "Optional chaining with null should return None");
96 }
97
98 #[test]
99 fn test_optional_chaining_with_undefined_returns_none() {
100 let opt_chain = make_optional_member_expr(make_undefined_expr(), "prop");
101 let (_confident, has_value) = evaluate_expr(&opt_chain);
102 assert!(
103 !has_value,
104 "Optional chaining with undefined should return None"
105 );
106 }
107
108 #[test]
109 fn test_optional_chaining_with_variable_not_confident() {
110 let opt_chain = make_optional_member_expr(make_ident_expr("obj"), "prop");
111 let (confident, _has_value) = evaluate_expr(&opt_chain);
112 assert!(
113 !confident,
114 "Optional chaining with variable should not be confident"
115 );
116 }
117
118 #[test]
119 fn test_optional_chaining_null_no_panic() {
120 let opt_chain = make_optional_member_expr(create_null_expr(), "nonexistent");
121 let (_confident, has_value) = evaluate_expr(&opt_chain);
122 assert!(!has_value, "Should short-circuit without panic");
123 }
124
125 #[test]
126 fn test_optional_chaining_with_nested_member() {
127 let opt_chain = make_optional_member_expr(make_ident_expr("obj"), "nested");
128 let (confident, _has_value) = evaluate_expr(&opt_chain);
129 assert!(!confident, "Variable reference should not be confident");
130 }
131
132 #[test]
133 fn test_optional_chaining_null_short_circuit() {
134 let opt_chain = make_optional_member_expr(create_null_expr(), "complexProp");
135 let (_confident, has_value) = evaluate_expr(&opt_chain);
136 assert!(!has_value, "Should short-circuit on null");
137 }
138
139 #[test]
140 fn test_optional_chaining_multiple_levels() {
141 let chain = make_optional_member_expr(
142 make_optional_member_expr(make_optional_member_expr(make_ident_expr("obj"), "a"), "b"),
143 "c",
144 );
145 let (confident, _has_value) = evaluate_expr(&chain);
146 assert!(!confident, "Variable reference should not be confident");
147 }
148
149 #[test]
150 fn test_optional_chaining_undefined_short_circuits() {
151 let opt_chain = make_optional_member_expr(make_undefined_expr(), "prop");
152 let (_confident, has_value) = evaluate_expr(&opt_chain);
153 assert!(!has_value, "Should short-circuit on undefined");
154 }
155
156 #[test]
157 fn test_optional_chaining_computed_property() {
158 let opt_chain = Expr::OptChain(OptChainExpr {
159 span: DUMMY_SP,
160 optional: true,
161 base: Box::new(OptChainBase::Member(MemberExpr {
162 span: DUMMY_SP,
163 obj: Box::new(make_ident_expr("obj")),
164 prop: MemberProp::Computed(ComputedPropName {
165 span: DUMMY_SP,
166 expr: Box::new(create_string_expr("prop")),
167 }),
168 })),
169 });
170 let (confident, _has_value) = evaluate_expr(&opt_chain);
171 assert!(!confident, "Variable reference should not be confident");
172 }
173
174 #[test]
175 fn test_optional_vs_regular_member_access() {
176 let regular = make_member_expr(make_ident_expr("obj"), "prop");
177 let optional = make_optional_member_expr(make_ident_expr("obj"), "prop");
178
179 let (regular_confident, _) = evaluate_expr(®ular);
180 let (optional_confident, _) = evaluate_expr(&optional);
181
182 assert!(
183 !regular_confident,
184 "Regular member with variable should not be confident"
185 );
186 assert!(
187 !optional_confident,
188 "Optional member with variable should not be confident"
189 );
190 }
191
192 #[test]
195 fn test_await_with_variable() {
196 let await_expr = make_await_expr(make_ident_expr("someVar"));
197 let (confident, _has_value) = evaluate_expr(&await_expr);
198 assert!(!confident, "Variable reference should not be confident");
199 }
200
201 #[test]
202 fn test_await_with_null() {
203 let await_expr = make_await_expr(create_null_expr());
204 let (_confident, has_value) = evaluate_expr(&await_expr);
205 assert!(has_value, "Null literal should evaluate to a value");
206 }
207
208 #[test]
209 fn test_await_with_number() {
210 let await_expr = make_await_expr(create_number_expr(42.0));
211 let (_confident, has_value) = evaluate_expr(&await_expr);
212 assert!(has_value, "Number literal should evaluate to a value");
213 }
214
215 #[test]
216 fn test_await_with_string() {
217 let await_expr = make_await_expr(create_string_expr("hello"));
218 let (_confident, has_value) = evaluate_expr(&await_expr);
219 assert!(has_value, "String literal should evaluate to a value");
220 }
221
222 #[test]
223 fn test_await_with_boolean() {
224 let await_expr = make_await_expr(create_bool_expr(true));
225 let (_confident, has_value) = evaluate_expr(&await_expr);
226 assert!(has_value, "Boolean literal should evaluate to a value");
227 }
228
229 #[test]
230 fn test_await_removes_await_keyword() {
231 let await_expr = make_await_expr(create_number_expr(123.0));
233 let (_confident, has_value) = evaluate_expr(&await_expr);
234 assert!(has_value, "Await should evaluate its argument");
236 }
237
238 #[test]
239 fn test_await_with_optional_chaining() {
240 let optional_member = make_optional_member_expr(make_ident_expr("obj"), "prop");
242 let await_expr = make_await_expr(optional_member);
243 let (confident, _has_value) = evaluate_expr(&await_expr);
244 assert!(!confident, "Variable in await should not be confident");
245 }
246
247 #[test]
248 fn test_await_with_null_optional_chaining() {
249 let optional_member = make_optional_member_expr(create_null_expr(), "prop");
251 let await_expr = make_await_expr(optional_member);
252 let (_confident, has_value) = evaluate_expr(&await_expr);
253 assert!(!has_value, "Short-circuit should propagate through await");
254 }
255
256 #[test]
259 fn test_null_literal_evaluation() {
260 let null_expr = create_null_expr();
261 let (confident, has_value) = evaluate_expr(&null_expr);
262 assert!(confident, "Null literal should be confident");
263 assert!(has_value, "Null literal should have a value");
264 }
265
266 #[test]
267 fn test_undefined_identifier_evaluation() {
268 let undef_expr = make_undefined_expr();
269 let (confident, has_value) = evaluate_expr(&undef_expr);
270 assert!(confident, "Undefined identifier should be confident");
271 assert!(has_value, "Undefined identifier should have a value");
272 }
273
274 #[test]
275 fn test_number_literal_evaluation() {
276 let num_expr = create_number_expr(42.0);
277 let (confident, has_value) = evaluate_expr(&num_expr);
278 assert!(confident, "Number literal should be confident");
279 assert!(has_value, "Number literal should have a value");
280 }
281
282 #[test]
283 fn test_string_literal_evaluation() {
284 let str_expr = create_string_expr("test");
285 let (confident, has_value) = evaluate_expr(&str_expr);
286 assert!(confident, "String literal should be confident");
287 assert!(has_value, "String literal should have a value");
288 }
289
290 #[test]
291 fn test_boolean_literal_evaluation() {
292 let bool_expr = create_bool_expr(true);
293 let (confident, has_value) = evaluate_expr(&bool_expr);
294 assert!(confident, "Boolean literal should be confident");
295 assert!(has_value, "Boolean literal should have a value");
296 }
297
298 #[test]
299 fn test_variable_reference_not_confident() {
300 let var_expr = make_ident_expr("myVar");
301 let (confident, _has_value) = evaluate_expr(&var_expr);
302 assert!(!confident, "Variable reference should not be confident");
303 }
304
305 #[test]
308 fn test_unary_minus_number() {
309 let unary_expr = make_unary_expr(UnaryOp::Minus, create_number_expr(5.0));
310 let (confident, has_value) = evaluate_expr(&unary_expr);
311 assert!(confident, "Unary minus on number should be confident");
312 assert!(has_value, "Unary minus should have a value");
313 }
314
315 #[test]
316 fn test_unary_plus_number() {
317 let unary_expr = make_unary_expr(UnaryOp::Plus, create_number_expr(5.0));
318 let (confident, has_value) = evaluate_expr(&unary_expr);
319 assert!(confident, "Unary plus on number should be confident");
320 assert!(has_value, "Unary plus should have a value");
321 }
322
323 #[test]
324 fn test_unary_not_boolean() {
325 let unary_expr = make_unary_expr(UnaryOp::Bang, create_bool_expr(true));
326 let (confident, has_value) = evaluate_expr(&unary_expr);
327 assert!(confident, "Unary not on boolean should be confident");
328 assert!(has_value, "Unary not should have a value");
329 }
330
331 #[test]
332 fn test_unary_minus_variable() {
333 let unary_expr = make_unary_expr(UnaryOp::Minus, make_ident_expr("x"));
334 let (confident, _has_value) = evaluate_expr(&unary_expr);
335 assert!(
336 !confident,
337 "Unary minus on variable should not be confident"
338 );
339 }
340
341 #[test]
344 fn test_binary_addition_numbers() {
345 let bin_expr = make_binary_expr(
346 create_number_expr(5.0),
347 BinaryOp::Add,
348 create_number_expr(3.0),
349 );
350 let (confident, has_value) = evaluate_expr(&bin_expr);
351 assert!(confident, "Binary addition of numbers should be confident");
352 assert!(has_value, "Binary addition should have a value");
353 }
354
355 #[test]
356 fn test_binary_subtraction_numbers() {
357 let bin_expr = make_binary_expr(
358 create_number_expr(5.0),
359 BinaryOp::Sub,
360 create_number_expr(3.0),
361 );
362 let (confident, has_value) = evaluate_expr(&bin_expr);
363 assert!(
364 confident,
365 "Binary subtraction of numbers should be confident"
366 );
367 assert!(has_value, "Binary subtraction should have a value");
368 }
369
370 #[test]
371 fn test_binary_multiplication_numbers() {
372 let bin_expr = make_binary_expr(
373 create_number_expr(5.0),
374 BinaryOp::Mul,
375 create_number_expr(3.0),
376 );
377 let (confident, has_value) = evaluate_expr(&bin_expr);
378 assert!(
379 confident,
380 "Binary multiplication of numbers should be confident"
381 );
382 assert!(has_value, "Binary multiplication should have a value");
383 }
384
385 #[test]
386 fn test_binary_with_variable() {
387 let bin_expr = make_binary_expr(make_ident_expr("x"), BinaryOp::Add, create_number_expr(5.0));
388 let (confident, _has_value) = evaluate_expr(&bin_expr);
389 assert!(
390 !confident,
391 "Binary expression with variable should not be confident"
392 );
393 }
394
395 #[test]
396 fn test_binary_logical_and() {
397 let bin_expr = make_binary_expr(
398 create_bool_expr(true),
399 BinaryOp::LogicalAnd,
400 create_bool_expr(false),
401 );
402 let (_confident, has_value) = evaluate_expr(&bin_expr);
403 assert!(
404 has_value || !_confident,
405 "Logical AND should either have value or not be confident"
406 );
407 }
408
409 #[test]
410 fn test_binary_logical_or() {
411 let bin_expr = make_binary_expr(
412 create_bool_expr(true),
413 BinaryOp::LogicalOr,
414 create_bool_expr(false),
415 );
416 let (_confident, has_value) = evaluate_expr(&bin_expr);
417 assert!(
418 has_value || !_confident,
419 "Logical OR should either have value or not be confident"
420 );
421 }
422
423 #[test]
426 fn test_member_access_with_variable() {
427 let member_expr = make_member_expr(make_ident_expr("obj"), "prop");
428 let (confident, _has_value) = evaluate_expr(&member_expr);
429 assert!(
430 !confident,
431 "Member access on variable should not be confident"
432 );
433 }
434
435 #[test]
436 fn test_member_access_nested() {
437 let nested = make_member_expr(make_member_expr(make_ident_expr("obj"), "a"), "b");
438 let (confident, _has_value) = evaluate_expr(&nested);
439 assert!(
440 !confident,
441 "Nested member access on variable should not be confident"
442 );
443 }
444
445 #[test]
448 fn test_nested_optional_chaining() {
449 let nested = make_optional_member_expr(
450 make_optional_member_expr(make_ident_expr("obj"), "prop1"),
451 "prop2",
452 );
453 let (confident, _has_value) = evaluate_expr(&nested);
454 assert!(
455 !confident,
456 "Nested optional chains should not be confident with variables"
457 );
458 }
459
460 #[test]
461 fn test_await_then_optional_chain() {
462 let optional = make_optional_member_expr(create_null_expr(), "prop");
463 let await_expr = make_await_expr(optional);
464 let (_confident, has_value) = evaluate_expr(&await_expr);
465 assert!(
466 !has_value,
467 "Optional chain short-circuit should propagate through await"
468 );
469 }
470
471 #[test]
472 fn test_unary_on_optional_chain() {
473 let optional = make_optional_member_expr(make_ident_expr("obj"), "prop");
474 let unary = make_unary_expr(UnaryOp::Bang, optional);
475 let (confident, _has_value) = evaluate_expr(&unary);
476 assert!(
477 !confident,
478 "Unary on optional chain should not be confident with variable"
479 );
480 }
481
482 #[test]
483 fn test_binary_on_optional_chains() {
484 let opt1 = make_optional_member_expr(make_ident_expr("a"), "x");
485 let opt2 = make_optional_member_expr(make_ident_expr("b"), "y");
486 let binary = make_binary_expr(opt1, BinaryOp::Add, opt2);
487 let (confident, _has_value) = evaluate_expr(&binary);
488 assert!(
489 !confident,
490 "Binary on optional chains should not be confident with variables"
491 );
492 }
493
494 #[test]
495 fn test_multiple_levels_of_optional_chaining_with_null_at_end() {
496 let null_at_end = make_optional_member_expr(create_null_expr(), "prop");
497 let (_confident, has_value) = evaluate_expr(&null_at_end);
498 assert!(!has_value, "Should handle null at any level");
499 }
500
501 #[test]
502 fn test_await_with_multiple_chained_operations() {
503 let inner_await = make_await_expr(make_ident_expr("someVar"));
505 let outer_await = make_await_expr(inner_await);
506 let (confident, _has_value) = evaluate_expr(&outer_await);
507 assert!(
508 !confident,
509 "Nested awaits with variables should not be confident"
510 );
511 }
512
513 #[test]
514 fn test_complex_expression_await_optional_unary() {
515 let optional = make_optional_member_expr(make_ident_expr("obj"), "prop");
517 let unary = make_unary_expr(UnaryOp::Bang, optional);
518 let await_expr = make_await_expr(unary);
519 let (confident, _has_value) = evaluate_expr(&await_expr);
520 assert!(
521 !confident,
522 "Complex expression should not be confident with variables"
523 );
524 }
525
526 #[test]
527 fn test_null_coalescing_like_pattern() {
528 let optional = make_optional_member_expr(create_null_expr(), "prop");
530 let (_confident, has_value) = evaluate_expr(&optional);
531 assert!(!has_value, "Null coalescing pattern should short-circuit");
532 }
533
534 #[test]
537 fn test_null_literal_member_access() {
538 let member_expr = make_member_expr(create_null_expr(), "prop");
540 let (_confident, has_value) = evaluate_expr(&member_expr);
541 assert!(
543 has_value,
544 "Member access on null literal should return the literal value"
545 );
546 }
547
548 #[test]
549 fn test_number_literal_member_access() {
550 let member_expr = make_member_expr(create_number_expr(42.0), "prop");
552 let (_confident, has_value) = evaluate_expr(&member_expr);
553 assert!(
555 has_value,
556 "Member access on number literal should return the literal value"
557 );
558 }
559
560 #[test]
561 fn test_boolean_literal_member_access() {
562 let member_expr = make_member_expr(create_bool_expr(true), "prop");
564 let (_confident, has_value) = evaluate_expr(&member_expr);
565 assert!(
567 has_value,
568 "Member access on boolean literal should return the literal value"
569 );
570 }
571
572 #[test]
573 fn test_string_literal_member_access() {
574 let member_expr = make_member_expr(create_string_expr("test"), "prop");
576 let (_confident, has_value) = evaluate_expr(&member_expr);
577 assert!(
579 has_value,
580 "Member access on string literal should return the literal value"
581 );
582 }
583
584 #[test]
585 fn test_literal_with_optional_chaining() {
586 let opt_chain = make_optional_member_expr(create_number_expr(5.0), "prop");
588 let (_confident, has_value) = evaluate_expr(&opt_chain);
589 assert!(
591 has_value,
592 "Optional member access on number literal should return the literal value"
593 );
594 }
595
596 fn make_regex_expr(pattern: &str, flags: &str) -> Expr {
600 Expr::Lit(swc_core::ecma::ast::Lit::Regex(Regex {
601 span: DUMMY_SP,
602 exp: pattern.into(),
603 flags: flags.into(),
604 }))
605 }
606
607 fn make_call_expr(callee: Expr, args: Vec<Expr>) -> Expr {
609 Expr::Call(CallExpr {
610 span: DUMMY_SP,
611 callee: Callee::Expr(Box::new(callee)),
612 args: args
613 .into_iter()
614 .map(|arg| ExprOrSpread {
615 spread: None,
616 expr: Box::new(arg),
617 })
618 .collect(),
619 type_args: None,
620 ..CallExpr::dummy()
621 })
622 }
623
624 #[test]
625 fn test_regex_literal_evaluation() {
626 let regex_expr = make_regex_expr("test", "g");
627 let (confident, has_value) = evaluate_expr(®ex_expr);
628 assert!(confident, "Regex literal should be confident");
629 assert!(has_value, "Regex literal should have a value");
630 }
631
632 #[test]
633 fn test_regex_with_escaped_chars() {
634 let regex_expr = make_regex_expr("\\/regex", "");
635 let (confident, has_value) = evaluate_expr(®ex_expr);
636 assert!(
637 confident,
638 "Regex with escaped characters should be confident"
639 );
640 assert!(
641 has_value,
642 "Regex with escaped characters should have a value"
643 );
644 }
645
646 #[test]
647 fn test_regex_with_flags() {
648 let regex_expr = make_regex_expr("test", "gi");
649 let (confident, has_value) = evaluate_expr(®ex_expr);
650 assert!(confident, "Regex with flags should be confident");
651 assert!(has_value, "Regex with flags should have a value");
652 }
653
654 #[test]
655 fn test_regex_without_flags() {
656 let regex_expr = make_regex_expr("^hello.*world$", "");
657 let (confident, has_value) = evaluate_expr(®ex_expr);
658 assert!(confident, "Regex without flags should be confident");
659 assert!(has_value, "Regex without flags should have a value");
660 }
661
662 #[test]
663 fn test_regex_test_method_with_variable() {
664 let regex_expr = make_regex_expr("test", "");
666 let member_expr = make_member_expr(regex_expr, "test");
667 let call_expr = make_call_expr(member_expr, vec![make_ident_expr("someVar")]);
668 let (confident, _has_value) = evaluate_expr(&call_expr);
669 assert!(
670 !confident,
671 "Regex.test() with variable should not be confident"
672 );
673 }
674
675 #[test]
676 fn test_regex_test_method_with_literal() {
677 let regex_expr = make_regex_expr("test", "");
679 let member_expr = make_member_expr(regex_expr, "test");
680 let call_expr = make_call_expr(member_expr, vec![create_string_expr("literal")]);
681 let (confident, _has_value) = evaluate_expr(&call_expr);
682 assert!(
683 !confident,
684 "Regex.test() cannot be statically evaluated, should not be confident"
685 );
686 }
687
688 #[test]
689 fn test_regex_exec_method() {
690 let regex_expr = make_regex_expr("test", "");
692 let member_expr = make_member_expr(regex_expr, "exec");
693 let call_expr = make_call_expr(member_expr, vec![make_ident_expr("someVar")]);
694 let (confident, _has_value) = evaluate_expr(&call_expr);
695 assert!(
696 !confident,
697 "Regex.exec() should not be confident (cannot be statically evaluated)"
698 );
699 }
700
701 #[test]
702 fn test_regex_match_method() {
703 let regex_expr = make_regex_expr("test", "");
705 let member_expr = make_member_expr(regex_expr, "match");
706 let call_expr = make_call_expr(member_expr, vec![]);
707 let (confident, _has_value) = evaluate_expr(&call_expr);
708 assert!(
709 !confident,
710 "Regex.match() should not be confident (cannot be statically evaluated)"
711 );
712 }
713
714 #[test]
715 fn test_complex_regex_pattern() {
716 let regex_expr = make_regex_expr("^[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z]{2,}$", "");
717 let (confident, has_value) = evaluate_expr(®ex_expr);
718 assert!(confident, "Complex regex pattern should be confident");
719 assert!(has_value, "Complex regex pattern should have a value");
720 }
721
722 #[test]
723 fn test_regex_with_unicode_flag() {
724 let regex_expr = make_regex_expr("\\p{Emoji}", "u");
725 let (confident, has_value) = evaluate_expr(®ex_expr);
726 assert!(confident, "Regex with unicode flag should be confident");
727 assert!(has_value, "Regex with unicode flag should have a value");
728 }
729
730 #[test]
731 fn test_regex_test_method_with_nullish_coalescing() {
732 let regex_expr = make_regex_expr("\\/test", "");
734 let member_expr = make_member_expr(regex_expr, "test");
735 let nullish = make_binary_expr(
737 make_ident_expr("pattern"),
738 BinaryOp::NullishCoalescing,
739 create_string_expr(""),
740 );
741 let call_expr = make_call_expr(member_expr, vec![nullish]);
742 let (confident, _has_value) = evaluate_expr(&call_expr);
743 assert!(
744 !confident,
745 "Regex.test() with nullish coalescing should not be confident"
746 );
747 }
748
749 fn make_array_expr(elems: Vec<Expr>) -> Expr {
755 Expr::Array(ArrayLit {
756 span: DUMMY_SP,
757 elems: elems
758 .into_iter()
759 .map(|e| {
760 Some(ExprOrSpread {
761 spread: None,
762 expr: Box::new(e),
763 })
764 })
765 .collect(),
766 })
767 }
768
769 fn evaluate_expr_with_globals(expr: &Expr) -> (bool, bool) {
771 let globals = Globals::default();
772 GLOBALS.set(&globals, || evaluate_expr(expr))
773 }
774
775 #[test]
776 fn test_unsupported_array_method_panic_includes_method_name() {
777 let array = make_array_expr(vec![create_number_expr(1.0)]);
781 let member = make_member_expr(array, "unsupported");
782 let call = make_call_expr(member, vec![]);
783
784 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
785 evaluate_expr_with_globals(&call);
786 }));
787
788 assert!(result.is_err(), "Should panic for unsupported array method");
789 let panic_msg = result
790 .unwrap_err()
791 .downcast_ref::<String>()
792 .cloned()
793 .unwrap_or_default();
794 assert!(
795 panic_msg.contains("unsupported"),
796 "Panic message should contain the method name 'unsupported', got: {}",
797 panic_msg
798 );
799 }
800
801 #[test]
802 fn test_unsupported_string_method_panic_includes_method_name() {
803 let string = create_string_expr("hello");
806 let member = make_member_expr(string, "unsupported");
807 let call = make_call_expr(member, vec![]);
808
809 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
810 evaluate_expr_with_globals(&call);
811 }));
812
813 assert!(
814 result.is_err(),
815 "Should panic for unsupported string method"
816 );
817 let panic_msg = result
818 .unwrap_err()
819 .downcast_ref::<String>()
820 .cloned()
821 .unwrap_or_default();
822 assert!(
823 panic_msg.contains("unsupported"),
824 "Panic message should contain the method name 'unsupported', got: {}",
825 panic_msg
826 );
827 }
828
829 #[test]
830 fn test_supported_array_methods_no_panic() {
831 let array = make_array_expr(vec![create_number_expr(1.0), create_number_expr(2.0)]);
834 let member = make_member_expr(array, "join");
835 let call = make_call_expr(member, vec![create_string_expr(",")]);
836
837 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
838 evaluate_expr_with_globals(&call);
839 }));
840
841 assert!(
842 result.is_ok(),
843 "Supported array method .join() should not panic"
844 );
845 }
846
847 fn evaluate_expr_full(expr: &Expr) -> (bool, Option<String>) {
852 let mut state_manager = StateManager::new(StyleXOptions::default());
853 let fns = FunctionMap::default();
854 let result = evaluate(expr, &mut state_manager, &fns);
855 (result.confident, result.reason)
856 }
857
858 #[test]
859 fn test_object_eval_failure_reason_includes_property_key() {
860 let obj = Expr::Object(ObjectLit {
862 span: DUMMY_SP,
863 props: vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
864 key: PropName::Ident(IdentName::new("color".into(), DUMMY_SP)),
865 value: Box::new(make_ident_expr("someVar")),
866 })))],
867 });
868
869 let (confident, reason) = evaluate_expr_full(&obj);
870 assert!(
871 !confident,
872 "Object with variable value should not be confident"
873 );
874 let reason_str = reason.expect("Should have a reason for the failure");
875 assert!(
876 reason_str.contains("color"),
877 "Reason should contain the property key 'color', got: {}",
878 reason_str
879 );
880 }
881
882 #[test]
883 fn test_object_eval_failure_reason_includes_nested_key() {
884 let obj = Expr::Object(ObjectLit {
886 span: DUMMY_SP,
887 props: vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
888 key: PropName::Ident(IdentName::new("backgroundColor".into(), DUMMY_SP)),
889 value: Box::new(make_ident_expr("dynamicValue")),
890 })))],
891 });
892
893 let (confident, reason) = evaluate_expr_full(&obj);
894 assert!(
895 !confident,
896 "Object with variable value should not be confident"
897 );
898 let reason_str = reason.expect("Should have a reason for the failure");
899 assert!(
900 reason_str.contains("backgroundColor"),
901 "Reason should contain the property key 'backgroundColor', got: {}",
902 reason_str
903 );
904 }
905}