Skip to main content

stylex_transform/shared/utils/js/tests/
evaluate_tests.rs

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  // ==================== HELPER FUNCTIONS ====================
22
23  // Helper: Create undefined expression
24  fn make_undefined_expr() -> Expr {
25    Expr::Ident(create_ident("undefined"))
26  }
27
28  // Helper: Create identifier expression
29  fn make_ident_expr(name: &str) -> Expr {
30    Expr::Ident(create_ident(name))
31  }
32
33  // Helper: Create regular member expression
34  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  // Helper: Create optional member expression
43  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  // Helper: Create unary expression (e.g., -5, !true, +x)
56  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  // Helper: Create binary expression (e.g., 5 + 3, x && y)
65  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  // Helper: Create await expression
75  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  // ==================== OPTIONAL CHAINING TESTS ====================
90
91  #[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(&regular);
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  // ==================== AWAIT EXPRESSION TESTS ====================
193
194  #[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    // Test that await evaluates its argument (not the await keyword itself)
232    let await_expr = make_await_expr(create_number_expr(123.0));
233    let (_confident, has_value) = evaluate_expr(&await_expr);
234    // Should have a value since we evaluate the number
235    assert!(has_value, "Await should evaluate its argument");
236  }
237
238  #[test]
239  fn test_await_with_optional_chaining() {
240    // Test: await obj?.prop
241    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    // Test: await null?.prop
250    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  // ==================== LITERAL EXPRESSION TESTS ====================
257
258  #[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  // ==================== UNARY EXPRESSION TESTS ====================
306
307  #[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  // ==================== BINARY EXPRESSION TESTS ====================
342
343  #[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  // ==================== MEMBER EXPRESSION TESTS ====================
424
425  #[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  // ==================== COMPOSITE TESTS ====================
446
447  #[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    // await (await someVar)
504    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    // Complex: await !(obj?.prop)
516    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    // Pattern: null?.prop (similar to nullish coalescing behavior)
529    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  // ==================== LITERAL MEMBER ACCESS TESTS ====================
535
536  #[test]
537  fn test_null_literal_member_access() {
538    // When accessing a member on a literal, the literal itself is evaluated
539    let member_expr = make_member_expr(create_null_expr(), "prop");
540    let (_confident, has_value) = evaluate_expr(&member_expr);
541    // The literal is evaluable, so has_value should be true (the literal value)
542    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    // When accessing a member on a number literal, the number itself is returned
551    let member_expr = make_member_expr(create_number_expr(42.0), "prop");
552    let (_confident, has_value) = evaluate_expr(&member_expr);
553    // The literal is evaluable
554    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    // When accessing a member on a boolean literal, the boolean itself is returned
563    let member_expr = make_member_expr(create_bool_expr(true), "prop");
564    let (_confident, has_value) = evaluate_expr(&member_expr);
565    // The literal is evaluable
566    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    // When accessing a member on a string literal, the string itself is returned
575    let member_expr = make_member_expr(create_string_expr("test"), "prop");
576    let (_confident, has_value) = evaluate_expr(&member_expr);
577    // The literal is evaluable
578    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    // Optional chaining on a literal number: the number literal is returned
587    let opt_chain = make_optional_member_expr(create_number_expr(5.0), "prop");
588    let (_confident, has_value) = evaluate_expr(&opt_chain);
589    // The number is a literal so it can be evaluated
590    assert!(
591      has_value,
592      "Optional member access on number literal should return the literal value"
593    );
594  }
595
596  // ==================== REGEX EXPRESSION TESTS ====================
597
598  // Helper: Create regex literal expression
599  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  // Helper: Create call expression
608  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(&regex_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(&regex_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(&regex_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(&regex_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    // Test: /regex/.test(someVar)
665    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    // Test: /regex/.test("literal")
678    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    // Test: /regex/.exec(someVar)
691    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    // Test: /regex/.match()
704    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(&regex_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(&regex_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    // Test: /regex/.test(someVar ?? '')
733    let regex_expr = make_regex_expr("\\/test", "");
734    let member_expr = make_member_expr(regex_expr, "test");
735    // Using binary expression for nullish coalescing
736    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  // ==================== PANIC CONTEXT TESTS ====================
750  // These tests verify that panics in the member expression evaluation path
751  // include useful context (e.g., property names) rather than generic messages.
752
753  // Helper: Create array literal expression
754  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  // Helper: evaluate with SWC GLOBALS set (needed for stylex_panic_with_context! code paths)
770  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    // Calling an unsupported method on an array literal (e.g., [1].unsupported())
778    // should panic with a message that includes the method name.
779    // This validates that stylex_panic_with_context! is used in the member call evaluation path.
780    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    // Calling an unsupported method on a string literal (e.g., "hello".unsupported())
804    // should panic with a message that includes the method name.
805    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    // Calling supported methods like .join() should not panic during evaluation.
832    // (.map() and .filter() need callback args, but .join() can work without)
833    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  // ==================== ERROR REASON KEY-PATH TESTS ====================
848  // These tests verify that when evaluating an object expression with a
849  // non-static value, the error reason includes the property key name.
850
851  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    // Evaluating { color: someVar } should produce a reason that includes "color"
861    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    // Evaluating { backgroundColor: someVar } should include "backgroundColor" in reason
885    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}