use super::*;
use crate::content_graph::ContentGraphBuilder;
#[test]
fn test_fit_to_css_value() {
assert_eq!(Fit::Cover.to_css_value(), "cover");
assert_eq!(Fit::Contain.to_css_value(), "contain");
assert_eq!(Fit::Fill.to_css_value(), "fill");
assert_eq!(Fit::None.to_css_value(), "none");
assert_eq!(Fit::ScaleDown.to_css_value(), "scale-down");
}
#[test]
fn test_fit_from_keyword() {
assert_eq!(Fit::from_keyword("cover"), Some(Fit::Cover));
assert_eq!(Fit::from_keyword("contain"), Some(Fit::Contain));
assert_eq!(Fit::from_keyword("fill"), Some(Fit::Fill));
assert_eq!(Fit::from_keyword("none"), Some(Fit::None));
assert_eq!(Fit::from_keyword("scale-down"), Some(Fit::ScaleDown));
assert_eq!(Fit::from_keyword("scaledown"), Some(Fit::ScaleDown));
}
#[test]
fn test_fit_from_keyword_case_insensitive() {
assert_eq!(Fit::from_keyword("COVER"), Some(Fit::Cover));
assert_eq!(Fit::from_keyword("Contain"), Some(Fit::Contain));
assert_eq!(Fit::from_keyword("Scale-Down"), Some(Fit::ScaleDown));
assert_eq!(Fit::from_keyword("SCALEDOWN"), Some(Fit::ScaleDown));
}
#[test]
fn test_fit_from_keyword_unknown() {
assert_eq!(Fit::from_keyword("zoom"), None);
assert_eq!(Fit::from_keyword(""), None);
assert_eq!(Fit::from_keyword("cover "), None); }
#[test]
fn test_align_side_from_keyword() {
assert_eq!(AlignSide::from_keyword("align-left"), Some(AlignSide::Left));
assert_eq!(
AlignSide::from_keyword("align-right"),
Some(AlignSide::Right)
);
assert_eq!(AlignSide::from_keyword("alignleft"), Some(AlignSide::Left));
assert_eq!(
AlignSide::from_keyword("alignright"),
Some(AlignSide::Right)
);
assert_eq!(AlignSide::from_keyword("ALIGN-LEFT"), Some(AlignSide::Left));
assert_eq!(
AlignSide::from_keyword("AlignRight"),
Some(AlignSide::Right)
);
assert_eq!(AlignSide::from_keyword(""), None);
}
#[test]
fn test_align_side_from_keyword_bare_directional() {
assert_eq!(AlignSide::from_keyword("left"), Some(AlignSide::Left));
assert_eq!(AlignSide::from_keyword("right"), Some(AlignSide::Right));
assert_eq!(AlignSide::from_keyword("LEFT"), Some(AlignSide::Left));
assert_eq!(AlignSide::from_keyword("Right"), Some(AlignSide::Right));
}
#[test]
fn test_parse_attrs_bare_left_is_position() {
let attrs = parse_media_attrs("left");
assert_eq!(attrs.position, Some(Position::Left));
assert_eq!(attrs.align, None);
let attrs = parse_media_attrs("right");
assert_eq!(attrs.position, Some(Position::Right));
assert_eq!(attrs.align, None);
}
#[test]
fn test_align_side_css_class() {
assert_eq!(AlignSide::Left.css_class(), "moss-align-left");
assert_eq!(AlignSide::Right.css_class(), "moss-align-right");
}
#[test]
fn test_position_to_css_value() {
assert_eq!(Position::Center.to_css_value(), "center");
assert_eq!(Position::Left.to_css_value(), "left");
assert_eq!(Position::Right.to_css_value(), "right");
assert_eq!(Position::Top.to_css_value(), "top");
assert_eq!(Position::Bottom.to_css_value(), "bottom");
assert_eq!(Position::TopLeft.to_css_value(), "top left");
assert_eq!(Position::TopRight.to_css_value(), "top right");
assert_eq!(Position::BottomLeft.to_css_value(), "bottom left");
assert_eq!(Position::BottomRight.to_css_value(), "bottom right");
}
#[test]
fn test_position_from_keyword_single() {
assert_eq!(Position::from_keyword("center"), Some(Position::Center));
assert_eq!(Position::from_keyword("left"), Some(Position::Left));
assert_eq!(Position::from_keyword("right"), Some(Position::Right));
assert_eq!(Position::from_keyword("top"), Some(Position::Top));
assert_eq!(Position::from_keyword("bottom"), Some(Position::Bottom));
}
#[test]
fn test_position_from_keyword_compound() {
assert_eq!(Position::from_keyword("top-left"), Some(Position::TopLeft));
assert_eq!(
Position::from_keyword("top-right"),
Some(Position::TopRight)
);
assert_eq!(
Position::from_keyword("bottom-left"),
Some(Position::BottomLeft)
);
assert_eq!(
Position::from_keyword("bottom-right"),
Some(Position::BottomRight)
);
assert_eq!(Position::from_keyword("topleft"), Some(Position::TopLeft));
assert_eq!(
Position::from_keyword("bottomright"),
Some(Position::BottomRight)
);
assert_eq!(Position::from_keyword("top left"), Some(Position::TopLeft));
assert_eq!(
Position::from_keyword("bottom right"),
Some(Position::BottomRight)
);
}
#[test]
fn test_position_from_keyword_case_insensitive() {
assert_eq!(Position::from_keyword("CENTER"), Some(Position::Center));
assert_eq!(Position::from_keyword("Top-Left"), Some(Position::TopLeft));
assert_eq!(
Position::from_keyword("BOTTOMRIGHT"),
Some(Position::BottomRight)
);
}
#[test]
fn test_position_from_keyword_unknown() {
assert_eq!(Position::from_keyword("middle"), None);
assert_eq!(Position::from_keyword(""), None);
}
#[test]
fn test_media_attrs_is_empty() {
let empty = MediaAttrs {
fit: None,
position: None,
align: None,
color: None,
class_names: Vec::new(),
extra_attrs: BTreeMap::new(),
};
assert!(empty.is_empty());
let with_fit = MediaAttrs {
fit: Some(Fit::Cover),
position: None,
align: None,
color: None,
class_names: Vec::new(),
extra_attrs: BTreeMap::new(),
};
assert!(!with_fit.is_empty());
let with_pos = MediaAttrs {
fit: None,
position: Some(Position::Center),
align: None,
color: None,
class_names: Vec::new(),
extra_attrs: BTreeMap::new(),
};
assert!(!with_pos.is_empty());
}
#[test]
fn test_to_inline_style_empty() {
let attrs = MediaAttrs {
fit: None,
position: None,
align: None,
color: None,
class_names: Vec::new(),
extra_attrs: BTreeMap::new(),
};
assert_eq!(attrs.to_inline_style(), None);
}
#[test]
fn test_to_inline_style_fit_only() {
let attrs = MediaAttrs {
fit: Some(Fit::Contain),
position: None,
align: None,
color: None,
class_names: Vec::new(),
extra_attrs: BTreeMap::new(),
};
assert_eq!(attrs.to_inline_style(), Some("object-fit:contain".into()));
}
#[test]
fn test_to_inline_style_position_only() {
let attrs = MediaAttrs {
fit: None,
position: Some(Position::Left),
align: None,
color: None,
class_names: Vec::new(),
extra_attrs: BTreeMap::new(),
};
assert_eq!(attrs.to_inline_style(), Some("object-position:left".into()));
}
#[test]
fn test_to_inline_style_both() {
let attrs = MediaAttrs {
fit: Some(Fit::Cover),
position: Some(Position::TopLeft),
align: None,
color: None,
class_names: Vec::new(),
extra_attrs: BTreeMap::new(),
};
assert_eq!(
attrs.to_inline_style(),
Some("object-fit:cover;object-position:top left".into())
);
}
#[test]
fn test_strip_wikilink_with_brackets() {
assert_eq!(strip_wikilink("[[photo.jpg]]"), "photo.jpg");
assert_eq!(strip_wikilink("[[path/to/image.png]]"), "path/to/image.png");
}
#[test]
fn test_strip_wikilink_without_brackets() {
assert_eq!(strip_wikilink("photo.jpg"), "photo.jpg");
assert_eq!(strip_wikilink("path/to/image.png"), "path/to/image.png");
}
#[test]
fn test_strip_wikilink_with_pipe() {
assert_eq!(strip_wikilink("[[photo.jpg|cover]]"), "photo.jpg|cover");
}
#[test]
fn test_strip_wikilink_with_whitespace() {
assert_eq!(strip_wikilink(" [[photo.jpg]] "), "photo.jpg");
}
#[test]
fn test_strip_wikilink_partial_brackets() {
assert_eq!(strip_wikilink("[[photo.jpg"), "[[photo.jpg");
assert_eq!(strip_wikilink("photo.jpg]]"), "photo.jpg]]");
}
#[test]
fn test_strip_wikilink_empty() {
assert_eq!(strip_wikilink("[[]]"), "");
assert_eq!(strip_wikilink(""), "");
}
#[test]
fn test_split_pipe_with_pipe() {
assert_eq!(split_pipe("photo.jpg|cover"), ("photo.jpg", "cover"));
assert_eq!(
split_pipe("path/to/img.png|contain center"),
("path/to/img.png", "contain center")
);
}
#[test]
fn test_split_pipe_no_pipe() {
assert_eq!(split_pipe("photo.jpg"), ("photo.jpg", ""));
assert_eq!(split_pipe(""), ("", ""));
}
#[test]
fn test_split_pipe_multiple_pipes() {
assert_eq!(split_pipe("a|b|c"), ("a", "b|c"));
}
#[test]
fn test_split_pipe_pipe_at_edges() {
assert_eq!(split_pipe("|cover"), ("", "cover"));
assert_eq!(split_pipe("photo.jpg|"), ("photo.jpg", ""));
}
#[test]
fn test_parse_attrs_fit_only() {
let attrs = parse_media_attrs("cover");
assert_eq!(attrs.fit, Some(Fit::Cover));
assert_eq!(attrs.position, None);
}
#[test]
fn test_parse_attrs_position_only() {
let attrs = parse_media_attrs("center");
assert_eq!(attrs.fit, None);
assert_eq!(attrs.position, Some(Position::Center));
}
#[test]
fn test_parse_attrs_fit_and_position() {
let attrs = parse_media_attrs("contain left");
assert_eq!(attrs.fit, Some(Fit::Contain));
assert_eq!(attrs.position, Some(Position::Left));
}
#[test]
fn test_parse_attrs_two_word_position() {
let attrs = parse_media_attrs("top left");
assert_eq!(attrs.fit, None);
assert_eq!(attrs.position, Some(Position::TopLeft));
let attrs2 = parse_media_attrs("cover bottom right");
assert_eq!(attrs2.fit, Some(Fit::Cover));
assert_eq!(attrs2.position, Some(Position::BottomRight));
}
#[test]
fn test_parse_attrs_hyphenated_compound_position() {
let attrs = parse_media_attrs("top-right");
assert_eq!(attrs.fit, None);
assert_eq!(attrs.position, Some(Position::TopRight));
let attrs2 = parse_media_attrs("fill bottom-left");
assert_eq!(attrs2.fit, Some(Fit::Fill));
assert_eq!(attrs2.position, Some(Position::BottomLeft));
}
#[test]
fn test_parse_attrs_unknown_tokens_ignored() {
let attrs = parse_media_attrs("cover unknown-token left");
assert_eq!(attrs.fit, Some(Fit::Cover));
assert_eq!(attrs.position, Some(Position::Left));
}
#[test]
fn test_parse_attrs_empty_string() {
let attrs = parse_media_attrs("");
assert!(attrs.is_empty());
}
#[test]
fn test_parse_attrs_only_whitespace() {
let attrs = parse_media_attrs(" ");
assert!(attrs.is_empty());
}
#[test]
fn test_parse_attrs_all_unknown() {
let attrs = parse_media_attrs("foo bar baz");
assert!(attrs.is_empty());
}
#[test]
fn test_parse_attrs_case_insensitive() {
let attrs = parse_media_attrs("COVER CENTER");
assert_eq!(attrs.fit, Some(Fit::Cover));
assert_eq!(attrs.position, Some(Position::Center));
}
#[test]
fn test_parse_attrs_last_wins_for_duplicates() {
let attrs = parse_media_attrs("cover contain");
assert_eq!(attrs.fit, Some(Fit::Contain));
}
#[test]
fn test_parse_attrs_scale_down() {
let attrs = parse_media_attrs("scale-down");
assert_eq!(attrs.fit, Some(Fit::ScaleDown));
}
fn sample_graph() -> ContentGraph {
let mut b = ContentGraphBuilder::new();
b.add_file("images/photo.jpg", "images/photo");
b.add_file("assets/banner.png", "assets/banner");
b.add_file("posts/hello.md", "posts/hello");
b.build()
}
#[test]
fn test_resolve_simple_path() {
let graph = sample_graph();
let result = resolve_media_ref("photo.jpg", "posts/hello.md", &graph);
assert_eq!(result.path, "images/photo.jpg");
assert!(result.attrs.is_empty());
}
#[test]
fn test_resolve_with_attrs() {
let graph = sample_graph();
let result = resolve_media_ref("photo.jpg|cover center", "posts/hello.md", &graph);
assert_eq!(result.path, "images/photo.jpg");
assert_eq!(result.attrs.fit, Some(Fit::Cover));
assert_eq!(result.attrs.position, Some(Position::Center));
}
#[test]
fn test_resolve_wikilink() {
let graph = sample_graph();
let result = resolve_media_ref("[[photo.jpg|contain]]", "posts/hello.md", &graph);
assert_eq!(result.path, "images/photo.jpg");
assert_eq!(result.attrs.fit, Some(Fit::Contain));
}
#[test]
fn test_resolve_wikilink_no_attrs() {
let graph = sample_graph();
let result = resolve_media_ref("[[photo.jpg]]", "posts/hello.md", &graph);
assert_eq!(result.path, "images/photo.jpg");
assert!(result.attrs.is_empty());
}
#[test]
fn test_resolve_external_http() {
let graph = sample_graph();
let result = resolve_media_ref(
"https://example.com/img.jpg|cover",
"posts/hello.md",
&graph,
);
assert_eq!(result.path, "https://example.com/img.jpg");
assert_eq!(result.attrs.fit, Some(Fit::Cover));
}
#[test]
fn test_resolve_external_protocol_relative() {
let graph = sample_graph();
let result = resolve_media_ref("//cdn.example.com/img.jpg", "posts/hello.md", &graph);
assert_eq!(result.path, "//cdn.example.com/img.jpg");
}
#[test]
fn test_resolve_external_data_uri() {
let graph = sample_graph();
let result = resolve_media_ref("data:image/png;base64,abc", "posts/hello.md", &graph);
assert_eq!(result.path, "data:image/png;base64,abc");
}
#[test]
fn test_resolve_root_relative() {
let graph = sample_graph();
let result = resolve_media_ref("/images/photo.jpg|fill", "posts/hello.md", &graph);
assert_eq!(result.path, "images/photo.jpg");
assert_eq!(result.attrs.fit, Some(Fit::Fill));
}
#[test]
fn test_resolve_unresolved_fallback() {
let graph = sample_graph();
let result = resolve_media_ref("missing.jpg", "posts/hello.md", &graph);
assert_eq!(result.path, "missing.jpg");
assert!(result.attrs.is_empty());
}
#[test]
fn test_resolve_wikilink_with_two_word_position() {
let graph = sample_graph();
let result = resolve_media_ref("[[banner.png|cover top left]]", "posts/hello.md", &graph);
assert_eq!(result.path, "assets/banner.png");
assert_eq!(result.attrs.fit, Some(Fit::Cover));
assert_eq!(result.attrs.position, Some(Position::TopLeft));
}
#[test]
fn test_resolve_external_in_wikilink() {
let graph = sample_graph();
let result = resolve_media_ref(
"[[https://example.com/img.jpg|contain]]",
"posts/hello.md",
&graph,
);
assert_eq!(result.path, "https://example.com/img.jpg");
assert_eq!(result.attrs.fit, Some(Fit::Contain));
}
#[test]
fn test_resolve_path_with_spaces_trimmed() {
let graph = sample_graph();
let result = resolve_media_ref(" photo.jpg | cover ", "posts/hello.md", &graph);
assert_eq!(result.path, "images/photo.jpg");
assert_eq!(result.attrs.fit, Some(Fit::Cover));
}
#[test]
fn test_is_all_display_keywords_positions() {
assert!(is_all_display_keywords("left"));
assert!(is_all_display_keywords("right"));
assert!(is_all_display_keywords("center"));
assert!(is_all_display_keywords("top"));
assert!(is_all_display_keywords("bottom"));
assert!(is_all_display_keywords("top left"));
assert!(is_all_display_keywords("bottom right"));
}
#[test]
fn test_is_all_display_keywords_fits() {
assert!(is_all_display_keywords("cover"));
assert!(is_all_display_keywords("contain"));
assert!(is_all_display_keywords("fill"));
assert!(is_all_display_keywords("none"));
assert!(is_all_display_keywords("scale-down"));
}
#[test]
fn test_is_all_display_keywords_combined() {
assert!(is_all_display_keywords("contain left"));
assert!(is_all_display_keywords("cover top left"));
assert!(is_all_display_keywords("cover top-right"));
assert!(is_all_display_keywords("scale-down bottom-left"));
}
#[test]
fn test_is_all_display_keywords_rejects_non_keywords() {
assert!(!is_all_display_keywords("A beautiful sunset"));
assert!(!is_all_display_keywords("left side"));
assert!(!is_all_display_keywords(""));
assert!(!is_all_display_keywords(" "));
}
#[test]
fn test_html_escape_basic() {
assert_eq!(html_escape("hello"), "hello");
assert_eq!(html_escape("a&b"), "a&b");
assert_eq!(html_escape("a\"b"), "a"b");
assert_eq!(html_escape("a'b"), "a'b");
assert_eq!(html_escape("a<b>c"), "a<b>c");
assert_eq!(
html_escape("<div class=\"x\">&'</div>"),
"<div class="x">&'</div>"
);
}
#[test]
fn test_parse_media_attrs_align_alone() {
let attrs = parse_media_attrs("align-left");
assert_eq!(attrs.align, Some(AlignSide::Left));
assert_eq!(attrs.fit, None);
assert_eq!(attrs.position, None);
}
#[test]
fn test_parse_media_attrs_align_with_cover() {
let a = parse_media_attrs("cover align-right");
assert_eq!(a.fit, Some(Fit::Cover));
assert_eq!(a.align, Some(AlignSide::Right));
let b = parse_media_attrs("align-right cover");
assert_eq!(b, a);
}
#[test]
fn test_parse_media_attrs_align_last_wins() {
let attrs = parse_media_attrs("align-left align-right");
assert_eq!(attrs.align, Some(AlignSide::Right));
let attrs = parse_media_attrs("align-right align-left");
assert_eq!(attrs.align, Some(AlignSide::Left));
}
#[test]
fn test_is_all_display_keywords_align() {
assert!(is_all_display_keywords("align-left"));
assert!(is_all_display_keywords("align-right"));
assert!(is_all_display_keywords("cover align-left"));
assert!(is_all_display_keywords("align-left cover"));
assert!(is_all_display_keywords("align-left top"));
}
#[test]
fn test_match_width_token_recognized() {
assert_eq!(match_width_token("body"), Some("body"));
assert_eq!(match_width_token("wide"), Some("wide"));
assert_eq!(match_width_token("page"), Some("page"));
assert_eq!(match_width_token("screen"), Some("screen"));
assert_eq!(match_width_token("full"), Some("screen"));
}
#[test]
fn test_match_width_token_rejects_non_width() {
assert_eq!(match_width_token(""), None);
assert_eq!(match_width_token("BODY"), None);
assert_eq!(match_width_token("widely"), None);
assert_eq!(match_width_token("wide angle"), None);
assert_eq!(match_width_token("contain"), None);
assert_eq!(match_width_token("left"), None);
}
#[test]
fn test_extract_width_from_alias_single_segment_width() {
let (w, rest) = extract_width_from_alias("full");
assert_eq!(w, Some("screen"));
assert_eq!(rest, "");
}
#[test]
fn test_extract_width_from_alias_caption_only() {
let (w, rest) = extract_width_from_alias("A beautiful sunset");
assert_eq!(w, None);
assert_eq!(rest, "A beautiful sunset");
}
#[test]
fn test_extract_width_from_alias_caption_then_width() {
let (w, rest) = extract_width_from_alias("A nice photo|full");
assert_eq!(w, Some("screen"));
assert_eq!(rest, "A nice photo");
}
#[test]
fn test_extract_width_from_alias_width_then_caption() {
let (w, rest) = extract_width_from_alias("wide|A nice photo");
assert_eq!(w, Some("wide"));
assert_eq!(rest, "A nice photo");
}
#[test]
fn test_extract_width_from_alias_caption_with_width_word_not_shadowed() {
let (w, rest) = extract_width_from_alias("caption that says wide");
assert_eq!(w, None);
assert_eq!(rest, "caption that says wide");
}
#[test]
fn test_extract_width_from_alias_only_first_width_extracted() {
let (w, rest) = extract_width_from_alias("full|wide");
assert_eq!(w, Some("screen"));
assert_eq!(rest, "wide");
}
#[test]
fn test_extract_width_from_alias_segment_whitespace_trimmed() {
let (w, rest) = extract_width_from_alias("caption | full");
assert_eq!(w, Some("screen"));
assert_eq!(rest, "caption ");
}
#[test]
fn test_media_attrs_class_names_preserved() {
let attrs = MediaAttrs {
fit: None,
position: None,
align: None,
color: None,
class_names: vec!["theme-rounded".to_string(), "shadow-lg".to_string()],
extra_attrs: BTreeMap::new(),
};
assert!(!attrs.is_empty());
assert_eq!(
attrs.class_attr(),
Some("theme-rounded shadow-lg".to_string())
);
}
#[test]
fn test_media_attrs_class_names_compose_with_align() {
let attrs = MediaAttrs {
fit: None,
position: None,
align: Some(AlignSide::Left),
color: None,
class_names: vec!["theme-rounded".to_string()],
extra_attrs: BTreeMap::new(),
};
assert_eq!(
attrs.class_attr(),
Some("moss-align-left theme-rounded".to_string())
);
}
#[test]
fn test_media_attrs_extra_attrs_non_empty() {
let mut extras = BTreeMap::new();
extras.insert("data-zoom".to_string(), "true".to_string());
extras.insert("data-id".to_string(), "42".to_string());
let attrs = MediaAttrs {
fit: None,
position: None,
align: None,
color: None,
class_names: vec![],
extra_attrs: extras,
};
assert!(!attrs.is_empty());
let keys: Vec<&str> = attrs.extra_attrs.keys().map(String::as_str).collect();
assert_eq!(keys, vec!["data-id", "data-zoom"]);
}
#[test]
fn test_classify_image_alias_none() {
let c = classify_image_alias(None);
assert_eq!(c.display_keywords, None);
assert_eq!(c.caption, None);
}
#[test]
fn test_classify_image_alias_empty_is_none_never_some_empty() {
let c = classify_image_alias(Some(""));
assert_eq!(c.display_keywords, None);
assert_eq!(c.caption, None);
}
#[test]
fn test_classify_image_alias_structural_single_keyword() {
let c = classify_image_alias(Some("cover"));
assert_eq!(c.display_keywords.as_deref(), Some("cover"));
assert_eq!(c.caption, None);
}
#[test]
fn test_classify_image_alias_structural_compound() {
let c = classify_image_alias(Some("wide cover"));
assert_eq!(c.display_keywords.as_deref(), Some("wide cover"));
assert_eq!(c.caption, None);
}
#[test]
fn test_classify_image_alias_pure_width_token_is_structural() {
let c = classify_image_alias(Some("wide"));
assert_eq!(c.display_keywords.as_deref(), Some("wide"));
assert_eq!(c.caption, None);
}
#[test]
fn test_classify_image_alias_caption_text() {
let c = classify_image_alias(Some("My nice photo"));
assert_eq!(c.display_keywords, None);
assert_eq!(c.caption.as_deref(), Some("My nice photo"));
}
#[test]
fn test_is_structural_alias_matches_classifier() {
assert!(is_structural_alias("cover"));
assert!(is_structural_alias("wide cover"));
assert!(is_structural_alias("top left"));
assert!(!is_structural_alias("My nice photo"));
assert!(!is_structural_alias(""));
}
#[test]
fn parse_image_width_named_tokens() {
assert_eq!(parse_image_width("wide").as_deref(), Some("wide"));
assert_eq!(parse_image_width("full").as_deref(), Some("screen")); assert_eq!(parse_image_width("body").as_deref(), Some("body"));
}
#[test]
fn parse_image_width_percent() {
assert_eq!(parse_image_width("55%").as_deref(), Some("55%"));
assert_eq!(parse_image_width("100%").as_deref(), Some("100%"));
assert_eq!(parse_image_width(" 40% ").as_deref(), Some("40%")); }
#[test]
fn parse_image_width_clamps_and_rejects() {
assert_eq!(parse_image_width("150%").as_deref(), Some("100%")); assert_eq!(parse_image_width("0%"), None); assert_eq!(parse_image_width("-5%"), None); assert_eq!(parse_image_width("50.5%").as_deref(), Some("50.5%")); }
#[test]
fn parse_image_width_rejects_non_width() {
assert_eq!(parse_image_width("wide angle photo"), None); assert_eq!(parse_image_width("200x150"), None); assert_eq!(parse_image_width("hello"), None);
assert_eq!(parse_image_width(""), None);
}
#[test]
fn split_alt_width_extracts_and_preserves() {
assert_eq!(
split_alt_width("My caption|55%"),
("My caption".to_string(), Some("55%".to_string()))
);
assert_eq!(
split_alt_width("55%"),
(String::new(), Some("55%".to_string()))
);
assert_eq!(
split_alt_width("wide"),
(String::new(), Some("wide".to_string()))
);
assert_eq!(
split_alt_width("just a caption"),
("just a caption".to_string(), None)
);
assert_eq!(
split_alt_width("cap|55%|extra"),
("cap|extra".to_string(), Some("55%".to_string()))
);
assert_eq!(
split_alt_width("40%|60%"),
("60%".to_string(), Some("40%".to_string()))
);
}
#[test]
fn set_image_width_standard_markdown() {
assert_eq!(
set_image_width("", Some("55%")),
""
);
assert_eq!(
set_image_width("", Some("55%")),
""
);
assert_eq!(
set_image_width("", Some("55%")),
""
);
assert_eq!(
set_image_width("", None),
""
);
assert_eq!(
set_image_width("", Some("55%")),
""
);
assert_eq!(
set_image_width("", Some("55%")),
""
);
}
#[test]
fn set_image_width_wikilink() {
assert_eq!(
set_image_width("![[pic.jpg]]", Some("55%")),
"![[pic.jpg|55%]]"
);
assert_eq!(
set_image_width("![[pic.jpg|30%]]", Some("55%")),
"![[pic.jpg|55%]]"
);
assert_eq!(
set_image_width("![[pic.jpg|wide]]", Some("55%")),
"![[pic.jpg|55%]]"
);
assert_eq!(set_image_width("![[pic.jpg|55%]]", None), "![[pic.jpg]]");
assert_eq!(
set_image_width("![[pic.jpg|My cap|30%]]", Some("55%")),
"![[pic.jpg|My cap|55%]]"
);
assert_eq!(
set_image_width("![[pic.jpg|My cap]]", Some("55%")),
"![[pic.jpg|My cap|55%]]"
);
}
#[test]
fn set_image_width_validates() {
assert_eq!(
set_image_width("", Some("150%")),
""
);
assert_eq!(
set_image_width("", Some("garbage")),
""
);
}
#[test]
fn set_image_width_passthrough_non_image() {
assert_eq!(set_image_width("plain text", Some("55%")), "plain text");
}
#[test]
fn parse_color_attr() {
let attrs = parse_media_attrs("color=black");
assert_eq!(attrs.color.as_deref(), Some("black"));
let attrs = parse_media_attrs("color=#0a0a0a");
assert_eq!(attrs.color.as_deref(), Some("#0a0a0a"));
}
#[test]
fn parse_color_attr_alongside_keywords() {
let attrs = parse_media_attrs("contain color=rgb(10,10,10) top left");
assert_eq!(attrs.color.as_deref(), Some("rgb(10,10,10)"));
assert!(attrs.fit.is_some(), "fit keyword must still parse");
assert!(
attrs.position.is_some(),
"position keywords must still parse"
);
}
#[test]
fn parse_empty_color_attr_is_none() {
let attrs = parse_media_attrs("color=");
assert_eq!(attrs.color, None);
assert!(attrs.is_empty());
}
#[test]
fn color_attr_alone_is_not_empty() {
let attrs = parse_media_attrs("color=black");
assert!(!attrs.is_empty());
}
#[test]
fn color_attr_does_not_leak_into_style_or_class() {
let attrs = parse_media_attrs("color=black");
assert_eq!(attrs.to_inline_style(), None);
assert_eq!(attrs.class_attr(), None);
}
#[test]
fn repeated_color_attr_last_wins() {
let attrs = parse_media_attrs("color=red color=blue");
assert_eq!(attrs.color.as_deref(), Some("blue"));
}