Skip to content

Commit f1890ef

Browse files
committed
Update coverage
1 parent c8e712d commit f1890ef

12 files changed

Lines changed: 230 additions & 91 deletions

File tree

bindings/devup-ui-wasm/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ where
3232
f(&mut guard)
3333
}
3434

35+
#[cfg(not(tarpaulin_include))]
3536
fn js_error(message: impl Display) -> JsValue {
3637
js_sys::Error::new(&message.to_string()).into()
3738
}

libs/css/src/lib.rs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -358,20 +358,13 @@ pub fn sheet_to_classname(
358358
num_to_nm_base(len)
359359
}
360360
});
361-
if filename.is_some() {
362-
if let Some(file_num) = file_num {
363-
let mut result = String::with_capacity(prefix.len() + 8 + clas_num.len());
364-
result.push_str(&prefix);
365-
result.push_str(&num_to_nm_base(file_num));
366-
result.push('-');
367-
result.push_str(&clas_num);
368-
result
369-
} else {
370-
let mut result = String::with_capacity(prefix.len() + clas_num.len());
371-
result.push_str(&prefix);
372-
result.push_str(&clas_num);
373-
result
374-
}
361+
if let Some(file_num) = file_num {
362+
let mut result = String::with_capacity(prefix.len() + 8 + clas_num.len());
363+
result.push_str(&prefix);
364+
result.push_str(&num_to_nm_base(file_num));
365+
result.push('-');
366+
result.push_str(&clas_num);
367+
result
375368
} else {
376369
let mut result = String::with_capacity(prefix.len() + clas_num.len());
377370
result.push_str(&prefix);

libs/css/src/optimize_value.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,12 @@ mod tests {
322322
#[case("translate(0px) scale(0deg)", "translate(0) scale(0)")]
323323
#[case("translate(-0px) scale(-0deg)", "translate(0) scale(0)")]
324324
#[case("translate(-10px) scale(-10deg)", "translate(-10px) scale(-10deg)")]
325+
// rgba/rgb fallback paths when channel parsing fails
326+
// i32 overflow for r/g/b (> i32::MAX = 2_147_483_647) → falls back to original
327+
#[case("rgba(2147483648,0,0,0.5)", "rgba(2147483648,0,0,.5)")]
328+
#[case("rgb(2147483648,0,0)", "rgb(2147483648,0,0)")]
329+
// f32 parse failure for alpha (".") → falls back to original
330+
#[case("rgba(255,0,0,.)", "rgba(255,0,0,.)")]
325331
fn test_optimize_value(#[case] input: &str, #[case] expected: &str) {
326332
assert_eq!(optimize_value(input), expected);
327333
}

libs/extractor/src/css_utils.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1436,4 +1436,18 @@ mod tests {
14361436
assert_eq!(result, expected_sorted);
14371437
}
14381438
}
1439+
1440+
#[rstest]
1441+
#[case(" hello", "hello")]
1442+
#[case("\t\nhello", "hello")]
1443+
#[case(" hello ", "hello")]
1444+
#[case("hello ", "hello")]
1445+
#[case("hello", "hello")]
1446+
#[case("", "")]
1447+
#[case(" ", "")]
1448+
fn test_trim_string_in_place(#[case] input: &str, #[case] expected: &str) {
1449+
let mut value = input.to_string();
1450+
trim_string_in_place(&mut value);
1451+
assert_eq!(value, expected);
1452+
}
14391453
}

libs/extractor/src/gen_class_name.rs

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -177,43 +177,44 @@ pub fn merge_expression_for_class_name<'a>(
177177
unknown_expr.push(expr);
178178
}
179179
}
180-
if unknown_expr.is_empty() && class_name.is_empty() {
181-
return None;
180+
if unknown_expr.is_empty() {
181+
if class_name.is_empty() {
182+
return None;
183+
}
184+
return Some(ast_builder.expression_string_literal(
185+
SPAN,
186+
ast_builder.str(&class_name),
187+
None,
188+
));
182189
}
183-
if !unknown_expr.is_empty() {
184-
if class_name.is_empty() && unknown_expr.len() == 1 {
185-
Some(unknown_expr.remove(0))
186-
} else {
187-
let mut qu = oxc_allocator::Vec::new_in(ast_builder.allocator);
188-
for idx in 0..=unknown_expr.len() {
189-
let tail = idx == unknown_expr.len();
190-
let t = TemplateElementValue {
191-
raw: ast_builder.str(if idx == 0 {
192-
if class_name.is_empty() {
193-
""
194-
} else {
195-
class_name.push(' ');
196-
class_name.as_str()
197-
}
198-
} else if tail {
190+
if class_name.is_empty() && unknown_expr.len() == 1 {
191+
Some(unknown_expr.remove(0))
192+
} else {
193+
let mut qu = oxc_allocator::Vec::new_in(ast_builder.allocator);
194+
for idx in 0..=unknown_expr.len() {
195+
let tail = idx == unknown_expr.len();
196+
let t = TemplateElementValue {
197+
raw: ast_builder.str(if idx == 0 {
198+
if class_name.is_empty() {
199199
""
200200
} else {
201-
" "
202-
}),
203-
cooked: None,
204-
};
205-
qu.push(ast_builder.template_element(SPAN, t, tail, false));
206-
}
207-
208-
Some(ast_builder.expression_template_literal(
209-
SPAN,
210-
qu,
211-
oxc_allocator::Vec::from_iter_in(unknown_expr, ast_builder.allocator),
212-
))
201+
class_name.push(' ');
202+
class_name.as_str()
203+
}
204+
} else if tail {
205+
""
206+
} else {
207+
" "
208+
}),
209+
cooked: None,
210+
};
211+
qu.push(ast_builder.template_element(SPAN, t, tail, false));
213212
}
214-
} else if class_name.is_empty() {
215-
None
216-
} else {
217-
Some(ast_builder.expression_string_literal(SPAN, ast_builder.str(&class_name), None))
213+
214+
Some(ast_builder.expression_template_literal(
215+
SPAN,
216+
qu,
217+
oxc_allocator::Vec::from_iter_in(unknown_expr, ast_builder.allocator),
218+
))
218219
}
219220
}

libs/extractor/src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6204,6 +6204,27 @@ import {Button} from '@devup/ui'
62046204
)
62056205
.unwrap()
62066206
));
6207+
6208+
// Computed selector key whose `key.name()` is None must be skipped.
6209+
// Mixed with a valid selector to ensure extraction still proceeds.
6210+
reset_class_map();
6211+
reset_file_map();
6212+
assert_debug_snapshot!(ToBTreeSet::from(
6213+
extract(
6214+
"test.jsx",
6215+
r#"import {Box} from '@devup-ui/core'
6216+
<Box selectors={{ [dynamic]: { color: 'red' }, "&:focus": { color: 'blue' } }} />
6217+
"#,
6218+
ExtractOption {
6219+
package: "@devup-ui/core".to_string(),
6220+
css_dir: "@devup-ui/core".to_string(),
6221+
single_css: true,
6222+
import_main_css: false,
6223+
import_aliases: HashMap::new()
6224+
}
6225+
)
6226+
.unwrap()
6227+
));
62076228
}
62086229

62096230
#[test]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
source: libs/extractor/src/lib.rs
3+
expression: "ToBTreeSet::from(extract(\"test.jsx\",\nr#\"import {Box} from '@devup-ui/core'\n<Box selectors={{ [dynamic]: { color: 'red' }, \"&:focus\": { color: 'blue' } }} />\n \"#,\nExtractOption\n{\n package: \"@devup-ui/core\".to_string(), css_dir:\n \"@devup-ui/core\".to_string(), single_css: true, import_main_css: false,\n import_aliases: HashMap::new()\n}).unwrap())"
4+
---
5+
ToBTreeSet {
6+
styles: {
7+
Static(
8+
ExtractStaticStyle {
9+
property: "color",
10+
value: "blue",
11+
level: 0,
12+
selector: Some(
13+
Selector(
14+
"&:focus",
15+
),
16+
),
17+
style_order: None,
18+
layer: None,
19+
},
20+
),
21+
},
22+
code: "import \"@devup-ui/core/devup-ui.css\";\n<div className=\"a\" />;\n",
23+
}

libs/extractor/src/stylex.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,16 +249,14 @@ fn compose_selectors(parts: &[SelectorPart]) -> Option<StyleSelector> {
249249
Some(format!("&{}", pseudos.join("")))
250250
};
251251

252-
if at_rules.is_empty() {
253-
pseudo_str.map(StyleSelector::Selector)
254-
} else if let Some((kind, query)) = at_rules.last() {
252+
if let Some((kind, query)) = at_rules.last() {
255253
Some(StyleSelector::At {
256254
kind: *kind,
257255
query: query.to_string(),
258256
selector: pseudo_str,
259257
})
260258
} else {
261-
None
259+
pseudo_str.map(StyleSelector::Selector)
262260
}
263261
}
264262

libs/extractor/src/tailwind.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,10 +1187,8 @@ fn is_valid_tailwind_value(value: &str) -> bool {
11871187
}
11881188

11891189
// Numeric values (including decimals like 0.5, 1.5)
1190-
let Some(first_char) = value.chars().next() else {
1191-
return false;
1192-
};
1193-
if first_char.is_ascii_digit() {
1190+
// Safe: `value.is_empty()` returns early above, so `value` has at least one char.
1191+
if value.starts_with(|c: char| c.is_ascii_digit()) {
11941192
return true;
11951193
}
11961194

libs/sheet/src/lib.rs

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,11 @@ use std::sync::LazyLock;
2121

2222
macro_rules! push_fmt {
2323
($target:expr, $($arg:tt)*) => {{
24-
std::fmt::Write::write_fmt($target, format_args!($($arg)*))
25-
.unwrap_or_else(|err| panic!("failed to write CSS into string: {err}"));
24+
// `std::fmt::Write::write_fmt` on `&mut String` is infallible; discard result.
25+
let _ = std::fmt::Write::write_fmt($target, format_args!($($arg)*));
2626
}};
2727
}
2828

29-
trait ExtractStyle {
30-
fn extract(&self) -> String;
31-
fn write_extract(&self, css: &mut String) {
32-
css.push_str(&self.extract());
33-
}
34-
}
35-
3629
#[derive(Debug, Hash, Eq, PartialEq, Deserialize, Serialize, Clone)]
3730
#[serde(rename_all = "camelCase")]
3831
pub struct StyleSheetProperty {
@@ -81,15 +74,7 @@ impl Ord for StyleSheetProperty {
8174
}
8275
}
8376

84-
impl ExtractStyle for StyleSheetProperty {
85-
fn extract(&self) -> String {
86-
let mut css = String::with_capacity(
87-
self.class_name.len() + self.property.len() + self.value.len() + 4,
88-
);
89-
self.write_extract(&mut css);
90-
css
91-
}
92-
77+
impl StyleSheetProperty {
9378
fn write_extract(&self, css: &mut String) {
9479
css.push_str(&merge_selector(&self.class_name, self.selector.as_ref()));
9580
css.push('{');
@@ -150,16 +135,6 @@ pub struct StyleSheetCss {
150135
pub css: String,
151136
}
152137

153-
impl ExtractStyle for StyleSheetCss {
154-
fn extract(&self) -> String {
155-
self.css.clone()
156-
}
157-
158-
fn write_extract(&self, css: &mut String) {
159-
css.push_str(&self.css);
160-
}
161-
}
162-
163138
type PropertyMap = BTreeMap<u8, BTreeMap<u8, FxHashSet<StyleSheetProperty>>>;
164139
type KeyframesMap = BTreeMap<String, BTreeMap<String, BTreeMap<String, Vec<(String, String)>>>>;
165140

@@ -2064,6 +2039,37 @@ mod tests {
20642039
"ShadowsInterface",
20652040
"ThemeInterface"
20662041
));
2042+
2043+
// Multiple typography keys + multiple color themes exercise the
2044+
// `plain_keys` semicolon separator (joins 2+ entries).
2045+
let mut sheet = StyleSheet::default();
2046+
let mut theme = Theme::default();
2047+
let mut light_theme = ColorTheme::default();
2048+
light_theme.add_color("primary", "#000");
2049+
let mut dark_theme = ColorTheme::default();
2050+
dark_theme.add_color("primary", "#fff");
2051+
theme.add_color_theme("default", light_theme);
2052+
theme.add_color_theme("dark", dark_theme);
2053+
let make_typography = || {
2054+
Typography::new(
2055+
Some("Arial".to_string()),
2056+
Some("16px".to_string()),
2057+
Some("400".to_string()),
2058+
Some("1.5".to_string()),
2059+
Some("0.5".to_string()),
2060+
)
2061+
};
2062+
theme.add_typography("heading", vec![Some(make_typography())]);
2063+
theme.add_typography("body", vec![Some(make_typography())]);
2064+
sheet.set_theme(theme);
2065+
assert_debug_snapshot!(sheet.create_interface(
2066+
"package",
2067+
"ColorInterface",
2068+
"TypographyInterface",
2069+
"LengthInterface",
2070+
"ShadowsInterface",
2071+
"ThemeInterface"
2072+
));
20672073
}
20682074

20692075
#[test]
@@ -2299,14 +2305,30 @@ mod tests {
22992305
}
23002306

23012307
#[test]
2302-
fn test_stylesheet_css_extract() {
2308+
fn test_stylesheet_css_struct() {
23032309
let css_entry = StyleSheetCss {
23042310
css: "div{display:flex}".to_string(),
23052311
};
2306-
assert_eq!(css_entry.extract(), "div{display:flex}");
2312+
assert_eq!(css_entry.css, "div{display:flex}");
23072313

23082314
let empty = StyleSheetCss { css: String::new() };
2309-
assert_eq!(empty.extract(), "");
2315+
assert_eq!(empty.css, "");
2316+
}
2317+
2318+
#[test]
2319+
fn test_stylesheet_property_ord_no_selectors() {
2320+
// Both sides without selectors: branches on property then value.
2321+
let make = |property: &str, value: &str| StyleSheetProperty {
2322+
class_name: "a".to_string(),
2323+
property: property.to_string(),
2324+
value: value.to_string(),
2325+
selector: None,
2326+
layer: None,
2327+
};
2328+
assert_eq!(make("color", "red").cmp(&make("color", "red")), Equal);
2329+
assert!(make("color", "red") < make("color", "white"));
2330+
assert!(make("color", "red") < make("display", "block"));
2331+
assert!(make("display", "block") > make("color", "white"));
23102332
}
23112333

23122334
#[test]

0 commit comments

Comments
 (0)