use markdown2pdf::config::{ConfigSource, load_config_strict};
use markdown2pdf::styling::{
Color, DocumentConfig, FontStyleVariant, FontWeight, PageSize, ResolveError, ResolvedStyle,
Sides, TextAlignment, available_theme_names, load_theme_preset, merge_documents, resolve,
};
#[test]
fn bundled_themes_all_load_cleanly() {
for name in available_theme_names() {
let cfg = load_theme_preset(name)
.unwrap_or_else(|e| panic!("preset `{}` failed to load: {}", name, e));
let _resolved: ResolvedStyle = resolve(cfg, None)
.unwrap_or_else(|e| panic!("preset `{}` failed to resolve: {}", name, e));
}
}
#[test]
fn default_theme_round_trips_into_resolved_style() {
let style = ResolvedStyle::default();
assert_eq!(style.paragraph.font_size_pt, 8.0);
assert_eq!(style.headings[0].font_size_pt, 14.0);
assert_eq!(style.headings[5].font_size_pt, 8.0);
assert_eq!(style.page.size, PageSize::A4);
assert!(matches!(style.page.size, PageSize::A4));
}
#[test]
fn color_deserializes_from_hex_struct_and_array() {
let hex = r##"[paragraph]
text_color = "#FF8800""##;
let s = load_config_strict(ConfigSource::Embedded(hex), None).unwrap();
assert_eq!(s.paragraph.text_color, Color::rgb(0xFF, 0x88, 0x00));
let short = r##"[paragraph]
text_color = "#F80""##;
let s = load_config_strict(ConfigSource::Embedded(short), None).unwrap();
assert_eq!(s.paragraph.text_color, Color::rgb(0xFF, 0x88, 0x00));
let strukt = r#"[paragraph]
text_color = { r = 10, g = 20, b = 30 }"#;
let s = load_config_strict(ConfigSource::Embedded(strukt), None).unwrap();
assert_eq!(s.paragraph.text_color, Color::rgb(10, 20, 30));
let array = r#"[paragraph]
text_color = [10, 20, 30]"#;
let s = load_config_strict(ConfigSource::Embedded(array), None).unwrap();
assert_eq!(s.paragraph.text_color, Color::rgb(10, 20, 30));
}
#[test]
fn sides_accepts_scalar_pair_quad_and_struct() {
let scalar = r#"[paragraph]
padding = 4.0"#;
let s = load_config_strict(ConfigSource::Embedded(scalar), None).unwrap();
assert_eq!(s.paragraph.padding, Sides { top: 4.0, right: 4.0, bottom: 4.0, left: 4.0 });
let pair = r#"[paragraph]
padding = [2.0, 6.0]"#;
let s = load_config_strict(ConfigSource::Embedded(pair), None).unwrap();
assert_eq!(s.paragraph.padding, Sides { top: 2.0, right: 6.0, bottom: 2.0, left: 6.0 });
let quad = r#"[paragraph]
padding = [1.0, 2.0, 3.0, 4.0]"#;
let s = load_config_strict(ConfigSource::Embedded(quad), None).unwrap();
assert_eq!(s.paragraph.padding, Sides { top: 1.0, right: 2.0, bottom: 3.0, left: 4.0 });
let strukt = r#"[paragraph]
padding = { top = 1.0, right = 2.0, bottom = 3.0, left = 4.0 }"#;
let s = load_config_strict(ConfigSource::Embedded(strukt), None).unwrap();
assert_eq!(s.paragraph.padding, Sides { top: 1.0, right: 2.0, bottom: 3.0, left: 4.0 });
}
#[test]
fn page_size_accepts_named_and_custom() {
let named = r#"[page]
size = "Letter""#;
let s = load_config_strict(ConfigSource::Embedded(named), None).unwrap();
assert_eq!(s.page.size, PageSize::Letter);
let custom = r#"[page]
size = { width_mm = 100.0, height_mm = 150.0 }"#;
let s = load_config_strict(ConfigSource::Embedded(custom), None).unwrap();
assert_eq!(s.page.size, PageSize::Custom { width_mm: 100.0, height_mm: 150.0 });
}
#[test]
fn font_weight_accepts_string_and_numeric() {
let bold = r#"[paragraph]
font_weight = "bold""#;
let s = load_config_strict(ConfigSource::Embedded(bold), None).unwrap();
assert_eq!(s.paragraph.font_weight, FontWeight::Bold);
let numeric = r#"[paragraph]
font_weight = 700"#;
let s = load_config_strict(ConfigSource::Embedded(numeric), None).unwrap();
assert_eq!(s.paragraph.font_weight, FontWeight::Numeric(700));
assert!(s.paragraph.is_bold(), "700 must count as bold");
let normal = r#"[paragraph]
font_weight = 400"#;
let s = load_config_strict(ConfigSource::Embedded(normal), None).unwrap();
assert!(!s.paragraph.is_bold(), "400 must not count as bold");
}
#[test]
fn inherits_resolves_recursively() {
let cfg = load_theme_preset("github").unwrap();
let resolved = resolve(cfg, None).unwrap();
assert_eq!(resolved.headings[5].font_size_pt, 10.5);
}
#[test]
fn user_config_overrides_preset_field_by_field() {
let user = r#"theme = "github"
[paragraph]
font_size_pt = 13.0"#;
let s = load_config_strict(ConfigSource::Embedded(user), None).unwrap();
assert_eq!(s.paragraph.font_size_pt, 13.0);
assert_eq!(s.headings[0].font_size_pt, 22.0);
}
#[test]
fn theme_override_arg_beats_user_theme_field() {
let user = r#"theme = "default""#;
let s = load_config_strict(ConfigSource::Embedded(user), Some("compact")).unwrap();
assert_eq!(s.paragraph.font_size_pt, 9.0);
}
#[test]
fn defaults_block_cascades_into_unset_block_fields() {
let user = r#"
[defaults]
font_family = "Times"
[paragraph]
font_size_pt = 12.0
"#;
let s = load_config_strict(ConfigSource::Embedded(user), None).unwrap();
assert_eq!(s.paragraph.font_family.as_deref(), Some("Times"));
assert_eq!(s.paragraph.font_size_pt, 12.0);
}
#[test]
fn unknown_field_raises_typed_error() {
let err = load_config_strict(
ConfigSource::Embedded(
r##"[paragraph]
texcolor = "#000000""##,
),
None,
);
assert!(matches!(err, Err(ResolveError::BadToml { .. })));
}
#[test]
fn unknown_theme_raises_typed_error() {
let err = load_config_strict(ConfigSource::Default, Some("notarealtheme"));
match err {
Err(ResolveError::UnknownTheme { name, .. }) => assert_eq!(name, "notarealtheme"),
other => panic!("expected UnknownTheme, got {:?}", other),
}
}
#[test]
fn merge_documents_overlay_wins_on_some_for_block_fields() {
let base: DocumentConfig = toml::from_str(
r#"
[paragraph]
font_size_pt = 8.0
font_weight = "normal"
"#,
)
.unwrap();
let overlay: DocumentConfig = toml::from_str(
r#"
[paragraph]
font_size_pt = 12.0
"#,
)
.unwrap();
let merged = merge_documents(base, overlay);
let p = merged.paragraph.unwrap();
assert_eq!(p.font_size_pt, Some(12.0));
assert_eq!(p.font_weight, Some(FontWeight::Normal));
}
#[test]
fn text_align_and_font_style_round_trip() {
let cfg = r#"[paragraph]
text_align = "justify"
font_style = "italic""#;
let s = load_config_strict(ConfigSource::Embedded(cfg), None).unwrap();
assert_eq!(s.paragraph.text_align, TextAlignment::Justify);
assert_eq!(s.paragraph.font_style, FontStyleVariant::Italic);
assert!(s.paragraph.is_italic());
}
#[test]
fn print_effective_config_round_trip() {
let resolved = load_config_strict(ConfigSource::Default, Some("academic")).unwrap();
let toml_text = toml::to_string(&resolved).expect("serialize resolved style");
assert!(toml_text.contains("font_size_pt"));
assert!(toml_text.contains("[page]"));
assert!(toml_text.contains("[paragraph]"));
}