1#[cfg(test)]
2mod tests {
3
4 use std::rc::Rc;
5
6 use indexmap::IndexMap;
7 use stylex_constants::constants::common::COMPILED_KEY;
8 use swc_core::{
9 common::DUMMY_SP,
10 ecma::{
11 ast::{MemberExpr, MemberProp},
12 utils::quote_ident,
13 },
14 };
15
16 use crate::shared::enums::data_structures::flat_compiled_styles_value::FlatCompiledStylesValue;
17 use crate::shared::utils::ast::convertors::{convert_lit_to_string, create_string_expr};
18 use crate::shared::utils::core::attrs::attrs;
19 use crate::shared::utils::core::js_to_expr::NestedStringObject;
20 use crate::shared::utils::core::parse_nullable_style::{ResolvedArg, StyleObject};
21 use crate::shared::utils::core::props::props;
22 use crate::shared::utils::core::stylex::stylex;
23 use stylex_ast::ast::factories::create_ident;
24
25 fn create_style_object_args(args: &[&[(&str, FlatCompiledStylesValue)]]) -> Vec<ResolvedArg> {
26 let mut result_args = vec![];
27 for arg in args.iter() {
28 let mut object = IndexMap::new();
30
31 for (key, value) in arg.iter() {
32 object.insert(key.to_string(), Rc::new(value.clone()));
33 }
34
35 result_args.push(ResolvedArg::StyleObject(
36 StyleObject::Style(object),
37 vec![create_ident("test")],
38 vec![MemberExpr {
39 span: DUMMY_SP,
40 obj: Box::new(create_string_expr("test")),
41 prop: MemberProp::Ident(quote_ident!("test")),
42 }],
43 ))
44 }
45
46 result_args
47 }
48
49 #[test]
50 fn stylex_inject() {
51 let args = create_style_object_args(&[&[
52 ("a", FlatCompiledStylesValue::String("aaa".into())),
53 ("b", FlatCompiledStylesValue::String("bbb".into())),
54 ("$$css", FlatCompiledStylesValue::Bool(true)),
55 ]]);
56
57 let result = stylex(&args).expect("Expected result to be Some");
59
60 let classname_string = result
62 .as_stylex()
63 .and_then(|expr| expr.as_lit())
64 .and_then(convert_lit_to_string)
65 .expect("Expected classname_string to be Some");
66
67 assert_eq!(classname_string, "aaa bbb");
68 }
69
70 #[test]
71 fn merge_order() {
72 let first = [
73 ("a", FlatCompiledStylesValue::String("a".into())),
74 (":hover__aa", FlatCompiledStylesValue::String("aa".into())),
75 ("$$css", FlatCompiledStylesValue::Bool(true)),
76 ];
77
78 let second = [
79 ("b", FlatCompiledStylesValue::String("b".into())),
80 ("$$css", FlatCompiledStylesValue::Bool(true)),
81 ];
82
83 let third = [
84 ("c", FlatCompiledStylesValue::String("c".into())),
85 (":hover__cc", FlatCompiledStylesValue::String("cc".into())),
86 ("$$css", FlatCompiledStylesValue::Bool(true)),
87 ];
88
89 let args = create_style_object_args(&[&first, &second, &third]);
90
91 let result = stylex(&args).expect("Expected result to be Some");
93
94 let classname_string = result
96 .as_stylex()
97 .and_then(|expr| expr.as_lit())
98 .and_then(convert_lit_to_string)
99 .expect("Expected classname_string to be Some");
100
101 assert_eq!(classname_string, "a aa b c cc");
102 }
103
104 #[test]
105 fn with_a_top_level_array_of_simple_overridden_classes() {
106 let first = [
107 (
108 "backgroundColor",
109 FlatCompiledStylesValue::String("nu7423ey".into()),
110 ),
111 ("$$css", FlatCompiledStylesValue::Bool(true)),
112 ];
113
114 let second = [
115 (
116 "backgroundColor",
117 FlatCompiledStylesValue::String("gh25dzvf".into()),
118 ),
119 ("$$css", FlatCompiledStylesValue::Bool(true)),
120 ];
121
122 let args = create_style_object_args(&[&first, &second]);
123
124 let result = stylex(&args).expect("Expected result to be Some");
126
127 let classname_string = result
129 .as_stylex()
130 .and_then(|expr| expr.as_lit())
131 .and_then(convert_lit_to_string)
132 .expect("Expected classname_string to be Some");
133
134 assert_eq!(classname_string, "gh25dzvf");
135 }
136
137 #[test]
138 fn with_nested_arrays_and_pseudo_classes_overriding_things() {
139 let first = [
140 (
141 "backgroundColor",
142 FlatCompiledStylesValue::String("nu7423ey".into()),
143 ),
144 ("$$css", FlatCompiledStylesValue::Bool(true)),
145 ];
146
147 let second = [
148 (
149 "backgroundColor",
150 FlatCompiledStylesValue::String("abcdefg".into()),
151 ),
152 (
153 ":hover__backgroundColor",
154 FlatCompiledStylesValue::String("ksdfmwjs".into()),
155 ),
156 ("$$css", FlatCompiledStylesValue::Bool(true)),
157 ];
158
159 let third = [
160 ("color", FlatCompiledStylesValue::String("gofk2cf1".into())),
161 (
162 ":hover__backgroundColor",
163 FlatCompiledStylesValue::String("rse6dlih".into()),
164 ),
165 ("$$css", FlatCompiledStylesValue::Bool(true)),
166 ];
167
168 let args = create_style_object_args(&[&first, &second, &third]);
169
170 let result = stylex(&args).expect("Expected result to be Some");
172
173 let classname_string = result
175 .as_stylex()
176 .and_then(|expr| expr.as_lit())
177 .and_then(convert_lit_to_string)
178 .expect("Expected classname_string to be Some");
179
180 assert_eq!(classname_string, "abcdefg gofk2cf1 rse6dlih");
181 }
182
183 #[test]
184 fn with_complicated_set_of_arguments() {
185 let styles = create_style_object_args(&[
186 &[
187 (
188 "backgroundColor",
189 FlatCompiledStylesValue::String("nu7423ey".into()),
190 ),
191 (
192 "borderColor",
193 FlatCompiledStylesValue::String("tpe1esc0".into()),
194 ),
195 (
196 "borderStyle",
197 FlatCompiledStylesValue::String("gewhe1h2".into()),
198 ),
199 (
200 "borderWidth",
201 FlatCompiledStylesValue::String("gcovof34".into()),
202 ),
203 (
204 "boxSizing",
205 FlatCompiledStylesValue::String("bdao358l".into()),
206 ),
207 (
208 "display",
209 FlatCompiledStylesValue::String("rse6dlih".into()),
210 ),
211 (
212 "listStyle",
213 FlatCompiledStylesValue::String("s5oniofx".into()),
214 ),
215 (
216 "marginTop",
217 FlatCompiledStylesValue::String("m8h3af8h".into()),
218 ),
219 (
220 "marginEnd",
221 FlatCompiledStylesValue::String("l7ghb35v".into()),
222 ),
223 (
224 "marginBottom",
225 FlatCompiledStylesValue::String("kjdc1dyq".into()),
226 ),
227 (
228 "marginStart",
229 FlatCompiledStylesValue::String("kmwttqpk".into()),
230 ),
231 (
232 "paddingTop",
233 FlatCompiledStylesValue::String("srn514ro".into()),
234 ),
235 (
236 "paddingEnd",
237 FlatCompiledStylesValue::String("oxkhqvkx".into()),
238 ),
239 (
240 "paddingBottom",
241 FlatCompiledStylesValue::String("rl78xhln".into()),
242 ),
243 (
244 "paddingStart",
245 FlatCompiledStylesValue::String("nch0832m".into()),
246 ),
247 (
248 "WebkitTapHighlightColor",
249 FlatCompiledStylesValue::String("qi72231t".into()),
250 ),
251 (
252 "textAlign",
253 FlatCompiledStylesValue::String("cr00lzj9".into()),
254 ),
255 (
256 "textDecoration",
257 FlatCompiledStylesValue::String("rn8ck1ys".into()),
258 ),
259 (
260 "whiteSpace",
261 FlatCompiledStylesValue::String("n3t5jt4f".into()),
262 ),
263 (
264 "wordWrap",
265 FlatCompiledStylesValue::String("gh25dzvf".into()),
266 ),
267 ("zIndex", FlatCompiledStylesValue::String("g4tp4svg".into())),
268 ("$$css", FlatCompiledStylesValue::Bool(true)),
269 ],
270 &[
271 ("cursor", FlatCompiledStylesValue::String("fsf7x5fv".into())),
272 (
273 "touchAction",
274 FlatCompiledStylesValue::String("s3jn8y49".into()),
275 ),
276 ("$$css", FlatCompiledStylesValue::Bool(true)),
277 ],
278 &[
279 (
280 "outline",
281 FlatCompiledStylesValue::String("icdlwmnq".into()),
282 ),
283 ("$$css", FlatCompiledStylesValue::Bool(true)),
284 ],
285 &[
286 (
287 "WebkitTapHighlightColor",
288 FlatCompiledStylesValue::String("oajrlxb2".into()),
289 ),
290 ("cursor", FlatCompiledStylesValue::String("nhd2j8a9".into())),
291 (
292 "touchAction",
293 FlatCompiledStylesValue::String("f1sip0of".into()),
294 ),
295 ("$$css", FlatCompiledStylesValue::Bool(true)),
296 ],
297 &[
298 (
299 "textDecoration",
300 FlatCompiledStylesValue::String("esuyzwwr".into()),
301 ),
302 (
303 ":hover__textDecoration",
304 FlatCompiledStylesValue::String("p8dawk7l".into()),
305 ),
306 ("$$css", FlatCompiledStylesValue::Bool(true)),
307 ],
308 &[
309 (
310 "backgroundColor",
311 FlatCompiledStylesValue::String("g5ia77u1".into()),
312 ),
313 ("border", FlatCompiledStylesValue::String("e4t7hp5w".into())),
314 ("color", FlatCompiledStylesValue::String("gmql0nx0".into())),
315 ("cursor", FlatCompiledStylesValue::String("nhd2j8a9".into())),
316 (
317 "display",
318 FlatCompiledStylesValue::String("q9uorilb".into()),
319 ),
320 (
321 "fontFamily",
322 FlatCompiledStylesValue::String("ihxqhq3m".into()),
323 ),
324 (
325 "fontSize",
326 FlatCompiledStylesValue::String("l94mrbxd".into()),
327 ),
328 (
329 "lineHeight",
330 FlatCompiledStylesValue::String("aenfhxwr".into()),
331 ),
332 (
333 "marginTop",
334 FlatCompiledStylesValue::String("kvgmc6g5".into()),
335 ),
336 (
337 "marginEnd",
338 FlatCompiledStylesValue::String("cxmmr5t8".into()),
339 ),
340 (
341 "marginBottom",
342 FlatCompiledStylesValue::String("oygrvhab".into()),
343 ),
344 (
345 "marginStart",
346 FlatCompiledStylesValue::String("hcukyx3x".into()),
347 ),
348 (
349 "paddingTop",
350 FlatCompiledStylesValue::String("jb3vyjys".into()),
351 ),
352 (
353 "paddingEnd",
354 FlatCompiledStylesValue::String("rz4wbd8a".into()),
355 ),
356 (
357 "paddingBottom",
358 FlatCompiledStylesValue::String("qt6c0cv9".into()),
359 ),
360 (
361 "paddingStart",
362 FlatCompiledStylesValue::String("a8nywdso".into()),
363 ),
364 (
365 "textAlign",
366 FlatCompiledStylesValue::String("i1ao9s8h".into()),
367 ),
368 (
369 "textDecoration",
370 FlatCompiledStylesValue::String("myohyog2".into()),
371 ),
372 (
373 ":hover__color",
374 FlatCompiledStylesValue::String("ksdfmwjs".into()),
375 ),
376 (
377 ":hover__textDecoration",
378 FlatCompiledStylesValue::String("gofk2cf1".into()),
379 ),
380 (
381 ":active__transform",
382 FlatCompiledStylesValue::String("lsqurvkf".into()),
383 ),
384 (
385 ":active__transition",
386 FlatCompiledStylesValue::String("bj9fd4vl".into()),
387 ),
388 ("$$css", FlatCompiledStylesValue::Bool(true)),
389 ],
390 &[
391 (
392 "display",
393 FlatCompiledStylesValue::String("a8c37x1j".into()),
394 ),
395 ("width", FlatCompiledStylesValue::String("k4urcfbm".into())),
396 ("$$css", FlatCompiledStylesValue::Bool(true)),
397 ],
398 &[
399 (
400 ":active__transform",
401 FlatCompiledStylesValue::String("tm8avpzi".into()),
402 ),
403 ("$$css", FlatCompiledStylesValue::Bool(true)),
404 ],
405 ]);
406
407 let result = stylex(&styles).expect("Expected result to be Some");
409 let repeat = stylex(&styles).expect("Expected result to be Some");
410
411 let classname_string = result
413 .as_stylex()
414 .and_then(|expr| expr.as_lit())
415 .and_then(convert_lit_to_string)
416 .expect("Expected classname_string to be Some");
417
418 let repeat_classname_string = repeat
419 .as_stylex()
420 .and_then(|expr| expr.as_lit())
421 .and_then(convert_lit_to_string)
422 .expect("Expected classname_string to be Some");
423
424 assert_eq!(classname_string, repeat_classname_string);
425
426 let mut parts: Vec<_> = classname_string.split_whitespace().collect();
427 parts.sort();
428 let result_classname_string = parts.join(" ");
429
430 let mut parts: Vec<_> = "g5ia77u1 tpe1esc0 gewhe1h2 gcovof34 bdao358l a8c37x1j s5oniofx kvgmc6g5 cxmmr5t8 oygrvhab hcukyx3x jb3vyjys rz4wbd8a qt6c0cv9 a8nywdso oajrlxb2 i1ao9s8h myohyog2 n3t5jt4f gh25dzvf g4tp4svg nhd2j8a9 f1sip0of icdlwmnq e4t7hp5w gmql0nx0 ihxqhq3m l94mrbxd aenfhxwr k4urcfbm gofk2cf1 ksdfmwjs tm8avpzi bj9fd4vl".split_whitespace().collect();
431 parts.sort();
432 let expected_classname_string = parts.join(" ");
433
434 assert_eq!(result_classname_string, expected_classname_string);
435 }
436
437 #[test]
438 fn data_prop_for_source_map_data() {
439 let first = [
440 (
441 "backgroundColor",
442 FlatCompiledStylesValue::String("backgroundColor-red".into()),
443 ),
444 (
445 "$$css",
446 FlatCompiledStylesValue::String("components/Foo.react.js:1".to_owned()),
447 ),
448 ];
449
450 let second = [
451 (
452 "color",
453 FlatCompiledStylesValue::String("color-blue".into()),
454 ),
455 (
456 "$$css",
457 FlatCompiledStylesValue::String("components/Bar.react.js:3".to_owned()),
458 ),
459 ];
460
461 let third = [
462 (
463 "display",
464 FlatCompiledStylesValue::String("display-block".into()),
465 ),
466 (
467 "$$css",
468 FlatCompiledStylesValue::String("components/Baz.react.js:5".to_owned()),
469 ),
470 ];
471
472 let args = create_style_object_args(&[&first, &second, &third]);
473
474 let binding = props(&args).expect("Expected result to be Some");
475 let props = binding.as_props().expect("Expected result to be Some");
476
477 let mut expected_props = IndexMap::new();
478
479 expected_props.insert(
480 "className".into(),
481 Rc::new(FlatCompiledStylesValue::String(
482 "backgroundColor-red color-blue display-block".into(),
483 )),
484 );
485 expected_props.insert(
486 "data-style-src".into(),
487 Rc::new(FlatCompiledStylesValue::String(
488 "components/Foo.react.js:1; components/Bar.react.js:3; components/Baz.react.js:5".into(),
489 )),
490 );
491
492 assert_eq!(
493 props,
494 &NestedStringObject::FlatCompiledStylesValues(expected_props)
495 );
496 }
497
498 #[test]
499 fn with_just_pseudoclasses() {
500 let args = create_style_object_args(&[
501 &[
502 (
503 ":hover__backgroundColor",
504 FlatCompiledStylesValue::String("rse6dlih".into()),
505 ),
506 ("$$css", FlatCompiledStylesValue::Bool(true)),
507 ],
508 &[
509 (
510 ":hover__color",
511 FlatCompiledStylesValue::String("gofk2cf1".into()),
512 ),
513 ("$$css", FlatCompiledStylesValue::Bool(true)),
514 ],
515 ]);
516
517 let result = stylex(&args).expect("Expected result to be Some");
518
519 let classname_string = result
520 .as_stylex()
521 .and_then(|expr| expr.as_lit())
522 .and_then(convert_lit_to_string)
523 .expect("Expected classname_string to be Some");
524
525 assert_eq!(classname_string, "rse6dlih gofk2cf1");
526 }
527
528 #[test]
529 fn props_with_dynamic_styles() {
530 let mut compiled = IndexMap::new();
532 compiled.insert(
533 "backgroundColor".to_string(),
534 Rc::new(FlatCompiledStylesValue::String(
535 "backgroundColor-red".into(),
536 )),
537 );
538 compiled.insert(
539 COMPILED_KEY.to_string(),
540 Rc::new(FlatCompiledStylesValue::String(
541 "components/Foo.react.js:1".into(),
542 )),
543 );
544
545 let mut dynamic_style = IndexMap::new();
547 dynamic_style.insert(
548 "color".to_string(),
549 Rc::new(FlatCompiledStylesValue::String("red".into())),
550 );
551
552 let args = vec![
553 ResolvedArg::StyleObject(
554 StyleObject::Style(compiled),
555 vec![create_ident("test")],
556 vec![MemberExpr {
557 span: DUMMY_SP,
558 obj: Box::new(create_string_expr("test")),
559 prop: MemberProp::Ident(quote_ident!("test")),
560 }],
561 ),
562 ResolvedArg::StyleObject(
563 StyleObject::Style(dynamic_style),
564 vec![create_ident("test")],
565 vec![MemberExpr {
566 span: DUMMY_SP,
567 obj: Box::new(create_string_expr("test")),
568 prop: MemberProp::Ident(quote_ident!("test")),
569 }],
570 ),
571 ];
572
573 let binding = props(&args).expect("Expected result to be Some");
574 let props_values = binding.as_props().expect("Expected props result");
575 let values = props_values.as_values().expect("Expected values map");
576
577 assert_eq!(
578 values.get("className").and_then(|v| {
579 if let FlatCompiledStylesValue::String(s) = v.as_ref() {
580 Some(s.as_str())
581 } else {
582 None
583 }
584 }),
585 Some("backgroundColor-red"),
586 );
587
588 let style_value = values.get("style").expect("Expected style key in props");
590 if let FlatCompiledStylesValue::KeyValues(pairs) = style_value.as_ref() {
591 assert_eq!(pairs.len(), 1);
592 assert_eq!(pairs[0].key, "color");
593 assert_eq!(pairs[0].value, "red");
594 } else {
595 panic!("Expected style to be KeyValues, got {:?}", style_value);
596 }
597
598 assert_eq!(
599 values.get("data-style-src").and_then(|v| {
600 if let FlatCompiledStylesValue::String(s) = v.as_ref() {
601 Some(s.as_str())
602 } else {
603 None
604 }
605 }),
606 Some("components/Foo.react.js:1"),
607 );
608 }
609
610 #[test]
615 fn attrs_basic_resolve() {
616 let args = create_style_object_args(&[&[
617 ("a", FlatCompiledStylesValue::String("aaa".into())),
618 ("b", FlatCompiledStylesValue::String("bbb".into())),
619 ("$$css", FlatCompiledStylesValue::Bool(true)),
620 ]]);
621
622 let binding = attrs(&args).expect("Expected result to be Some");
623 let attrs_result = binding.as_attrs().expect("Expected attrs result");
624
625 let mut expected = IndexMap::new();
626 expected.insert(
627 "class".into(),
628 Rc::new(FlatCompiledStylesValue::String("aaa bbb".into())),
629 );
630
631 assert_eq!(
632 attrs_result,
633 &NestedStringObject::FlatCompiledStylesValues(expected),
634 );
635 }
636
637 #[test]
638 fn attrs_with_dynamic_styles() {
639 let mut compiled = IndexMap::new();
641 compiled.insert(
642 "backgroundColor".to_string(),
643 Rc::new(FlatCompiledStylesValue::String(
644 "backgroundColor-red".into(),
645 )),
646 );
647 compiled.insert(
648 COMPILED_KEY.to_string(),
649 Rc::new(FlatCompiledStylesValue::String(
650 "components/Foo.react.js:1".into(),
651 )),
652 );
653
654 let mut dynamic_style = IndexMap::new();
656 dynamic_style.insert(
657 "color".to_string(),
658 Rc::new(FlatCompiledStylesValue::String("red".into())),
659 );
660 dynamic_style.insert(
661 "marginTop".to_string(),
662 Rc::new(FlatCompiledStylesValue::String("10px".into())),
663 );
664 dynamic_style.insert(
665 "opacity".to_string(),
666 Rc::new(FlatCompiledStylesValue::String("0.5".into())),
667 );
668 dynamic_style.insert(
669 "--foo".to_string(),
670 Rc::new(FlatCompiledStylesValue::String("2".into())),
671 );
672 dynamic_style.insert(
673 "MsTransition".to_string(),
674 Rc::new(FlatCompiledStylesValue::String("none".into())),
675 );
676 dynamic_style.insert(
677 "WebkitTapHighlightColor".to_string(),
678 Rc::new(FlatCompiledStylesValue::String("transparent".into())),
679 );
680
681 let args = vec![
682 ResolvedArg::StyleObject(
683 StyleObject::Style(compiled),
684 vec![create_ident("test")],
685 vec![MemberExpr {
686 span: DUMMY_SP,
687 obj: Box::new(create_string_expr("test")),
688 prop: MemberProp::Ident(quote_ident!("test")),
689 }],
690 ),
691 ResolvedArg::StyleObject(
692 StyleObject::Style(dynamic_style),
693 vec![create_ident("test")],
694 vec![MemberExpr {
695 span: DUMMY_SP,
696 obj: Box::new(create_string_expr("test")),
697 prop: MemberProp::Ident(quote_ident!("test")),
698 }],
699 ),
700 ];
701
702 let binding = attrs(&args).expect("Expected result to be Some");
703 let attrs_values = binding.as_attrs().expect("Expected attrs result");
704 let values = attrs_values.as_values().expect("Expected values map");
705
706 assert_eq!(
707 values.get("class").and_then(|v| {
708 if let FlatCompiledStylesValue::String(s) = v.as_ref() {
709 Some(s.as_str())
710 } else {
711 None
712 }
713 }),
714 Some("backgroundColor-red"),
715 );
716 assert_eq!(
717 values.get("style").and_then(|v| {
718 if let FlatCompiledStylesValue::String(s) = v.as_ref() {
719 Some(s.as_str())
720 } else {
721 None
722 }
723 }),
724 Some(
725 "color:red;margin-top:10px;opacity:0.5;--foo:2;-ms-transition:none;-webkit-tap-highlight-color:transparent"
726 ),
727 );
728
729 assert_eq!(
730 values.get("data-style-src").and_then(|v| {
731 if let FlatCompiledStylesValue::String(s) = v.as_ref() {
732 Some(s.as_str())
733 } else {
734 None
735 }
736 }),
737 Some("components/Foo.react.js:1"),
738 );
739 }
740
741 #[test]
744 fn legacy_merge_exposes_attrs() {
745 let args = create_style_object_args(&[&[
746 ("color", FlatCompiledStylesValue::String("color-red".into())),
747 ("$$css", FlatCompiledStylesValue::Bool(true)),
748 ]]);
749
750 let binding = attrs(&args).expect("Expected result to be Some");
751 let attrs_result = binding.as_attrs().expect("Expected attrs result");
752
753 let mut expected = IndexMap::new();
754 expected.insert(
755 "class".into(),
756 Rc::new(FlatCompiledStylesValue::String("color-red".into())),
757 );
758
759 assert_eq!(
760 attrs_result,
761 &NestedStringObject::FlatCompiledStylesValues(expected),
762 );
763 }
764}