cranpose-ui 0.1.164

UI primitives for Cranpose
Documentation
use super::*;

#[test]
fn a_redrawn_string_reuses_the_annotated_copy_from_last_frame() {
    let first = shared_plain_annotated_string("SCORE 340");
    let second = shared_plain_annotated_string("SCORE 340");
    assert!(Rc::ptr_eq(&first, &second));
    assert_eq!(second.text, "SCORE 340");
    assert!(second.span_styles.is_empty());
}

#[test]
fn distinct_strings_never_share_an_annotated_copy() {
    let first = shared_plain_annotated_string("READY");
    let second = shared_plain_annotated_string("GO");
    assert!(!Rc::ptr_eq(&first, &second));
    assert_eq!(first.text, "READY");
    assert_eq!(second.text, "GO");
}

#[test]
fn the_pool_survives_overflowing_its_capacity() {
    for index in 0..600 {
        let text = format!("distinct-{index}");
        let shared = shared_plain_annotated_string(&text);
        assert_eq!(shared.text, text);
    }
    let after = shared_plain_annotated_string("still correct");
    assert_eq!(after.text, "still correct");
}

#[test]
fn test_builder_span() {
    let span1 = SpanStyle {
        alpha: Some(0.5),
        ..Default::default()
    };

    let span2 = SpanStyle {
        alpha: Some(1.0),
        ..Default::default()
    };

    let annotated = AnnotatedString::builder()
        .append("Hello ")
        .push_style(span1.clone())
        .append("World")
        .push_style(span2.clone())
        .append("!")
        .pop()
        .pop()
        .to_annotated_string();

    assert_eq!(annotated.text, "Hello World!");
    assert_eq!(annotated.span_styles.len(), 2);
    assert_eq!(annotated.span_styles[0].range, 6..12);
    assert_eq!(annotated.span_styles[0].item, span1);
    assert_eq!(annotated.span_styles[1].range, 11..12);
    assert_eq!(annotated.span_styles[1].item, span2);
}

#[test]
fn with_link_url_roundtrips() {
    let url = "https://developer.android.com";
    let annotated = AnnotatedString::builder()
        .append("Visit ")
        .with_link(LinkAnnotation::Url(url.into()), |b| {
            b.append("Android Developers")
        })
        .append(".")
        .to_annotated_string();

    assert_eq!(annotated.text, "Visit Android Developers.");
    assert_eq!(annotated.link_annotations.len(), 1);
    let ann = &annotated.link_annotations[0];
    assert_eq!(ann.range, 6..24);
    assert_eq!(ann.item, LinkAnnotation::Url(url.into()));
}

#[test]
fn with_link_clickable_calls_handler() {
    use std::cell::Cell;
    let called = Rc::new(Cell::new(false));
    let called_clone = Rc::clone(&called);

    let annotated = AnnotatedString::builder()
        .with_link(
            LinkAnnotation::Clickable {
                tag: "action".into(),
                handler: Rc::new(move || called_clone.set(true)),
            },
            |b| b.append("click me"),
        )
        .to_annotated_string();

    assert_eq!(annotated.link_annotations.len(), 1);
    let ann = &annotated.link_annotations[0];
    if let LinkAnnotation::Clickable { handler, .. } = &ann.item {
        handler();
    }
    assert!(called.get(), "Clickable handler should have been called");
}

#[test]
fn with_link_subsequence_trims_range() {
    let annotated = AnnotatedString::builder()
        .append("pre ")
        .with_link(LinkAnnotation::Url("http://x.com".into()), |b| {
            b.append("link")
        })
        .append(" post")
        .to_annotated_string();

    let sub = annotated.subsequence(4..8);
    assert_eq!(sub.link_annotations.len(), 1);
    assert_eq!(sub.link_annotations[0].range, 0..4);
}

#[test]
fn append_annotated_preserves_ranges_with_existing_prefix() {
    let annotated = AnnotatedString::builder()
        .append("Hello ")
        .push_style(SpanStyle {
            alpha: Some(0.5),
            ..Default::default()
        })
        .append("World")
        .pop()
        .push_string_annotation("kind", "planet")
        .append("!")
        .pop()
        .to_annotated_string();

    let combined = AnnotatedString::builder()
        .append("Prefix ")
        .append_annotated(&annotated)
        .to_annotated_string();

    assert_eq!(combined.text, "Prefix Hello World!");
    assert_eq!(combined.span_styles.len(), 1);
    assert_eq!(combined.span_styles[0].range, 13..18);
    assert_eq!(combined.string_annotations.len(), 1);
    assert_eq!(combined.string_annotations[0].range, 18..19);
}

#[test]
fn append_annotated_subsequence_clips_ranges_to_slice() {
    let annotated = AnnotatedString::builder()
        .append("Before ")
        .push_style(SpanStyle {
            alpha: Some(0.5),
            ..Default::default()
        })
        .append("Styled")
        .pop()
        .with_link(LinkAnnotation::Url("https://example.com".into()), |b| {
            b.append(" Link")
        })
        .to_annotated_string();

    let slice = AnnotatedString::builder()
        .append("-> ")
        .append_annotated_subsequence(&annotated, 7..18)
        .to_annotated_string();

    assert_eq!(slice.text, "-> Styled Link");
    assert_eq!(slice.span_styles.len(), 1);
    assert_eq!(slice.span_styles[0].range, 3..9);
    assert_eq!(slice.link_annotations.len(), 1);
    assert_eq!(slice.link_annotations[0].range, 9..14);
}

#[test]
fn render_hash_changes_for_visual_style_ranges() {
    let plain = AnnotatedString::builder()
        .append("Hello")
        .to_annotated_string();
    let styled = AnnotatedString::builder()
        .push_style(SpanStyle {
            color: Some(crate::modifier::Color(1.0, 0.0, 0.0, 1.0)),
            ..Default::default()
        })
        .append("Hello")
        .pop()
        .to_annotated_string();

    assert_ne!(plain.render_hash(), styled.render_hash());
}