use std::cell::RefCell;
use std::rc::Rc;
use super::tests::{mount_feed, settle};
use super::*;
use crate::base::Size;
use crate::ui::UiTree;
use crate::widgets::itest_util::{click, mount_widget};
fn seed(feed: &FeedState) {
feed.push("a", FeedItem::text("alpha"));
feed.push("b", FeedItem::text("b one\nb two"));
}
#[test]
fn item_at_row_maps_rows_and_refuses_gaps_and_void() {
let size = Size::new(24, 8);
let (root, mut tree, feed) = mount_feed(size);
seed(&feed);
let _ = settle(&mut tree, size); assert_eq!(feed.item_at_row(0), Some(("a".into(), 0)));
assert_eq!(feed.item_at_row(1), None, "the gap row belongs to no one");
assert_eq!(feed.item_at_row(2), Some(("b".into(), 0)));
assert_eq!(feed.item_at_row(3), Some(("b".into(), 1)));
assert_eq!(feed.item_at_row(4), None, "past the tail");
assert_eq!(feed.item_at_row(-1), None, "negative rows");
root.dispose();
}
#[test]
fn item_at_row_is_none_before_the_first_draw_discovers_a_width() {
let (root, ()) = crate::reactive::create_root(|cx| {
let feed = FeedState::new(cx);
seed(&feed);
assert_eq!(feed.item_at_row(0), None);
});
root.dispose();
}
#[test]
fn on_item_press_reports_key_and_row_within_item() {
let size = Size::new(24, 8);
let log: Rc<RefCell<Vec<(String, i32)>>> = Rc::default();
let sink = log.clone();
let holder: Rc<RefCell<Option<FeedState>>> = Rc::default();
let h = holder.clone();
let (root, mut tree) = mount_widget(size, move |cx| {
let feed = FeedState::new(cx);
seed(&feed);
*h.borrow_mut() = Some(feed.clone());
crate::ui::Element::new()
.style(
LayoutStyle::default()
.width(Dimension::Percent(1.0))
.height(Dimension::Percent(1.0)),
)
.child(
Feed::new(&feed)
.on_item_press(move |key, row| sink.borrow_mut().push((key.into(), row)))
.view(cx),
)
.build()
});
let _ = settle(&mut tree, size);
click(&mut tree, 2, 0); click(&mut tree, 2, 1); click(&mut tree, 2, 2); click(&mut tree, 2, 3); click(&mut tree, 2, 6); assert_eq!(
log.borrow().as_slice(),
&[("a".into(), 0), ("b".into(), 0), ("b".into(), 1)],
"presses map to (key, row_within_item); gaps and void are silent"
);
root.dispose();
}
#[test]
fn press_callback_may_mutate_the_feed_reentrantly() {
let size = Size::new(24, 8);
let holder: Rc<RefCell<Option<FeedState>>> = Rc::default();
let h = holder.clone();
let (root, mut tree) = mount_widget(size, move |cx| {
let feed = FeedState::new(cx);
seed(&feed);
*h.borrow_mut() = Some(feed.clone());
let inner = feed.clone();
crate::ui::Element::new()
.style(
LayoutStyle::default()
.width(Dimension::Percent(1.0))
.height(Dimension::Percent(1.0)),
)
.child(
Feed::new(&feed)
.on_item_press(move |key, _| {
inner.push(key, FeedItem::text("toggled\nopen"));
})
.view(cx),
)
.build()
});
let _ = settle(&mut tree, size);
click(&mut tree, 2, 0); let canvas = settle(&mut tree, size);
let dump: Vec<String> = (0..size.h).map(|y| canvas.row_text(y)).collect();
assert!(
dump.iter().any(|r| r.contains("toggled")),
"reentrant mutation rendered:\n{dump:#?}"
);
root.dispose();
}
#[test]
fn on_item_press_may_dispose_the_feeds_scope() {
let size = Size::new(24, 8);
let mut tree = UiTree::new(size);
let holder: Rc<RefCell<Option<FeedState>>> = Rc::default();
let h = holder.clone();
let (root, ()) = crate::reactive::create_root(|cx| {
let pane_cx = cx.child();
let feed = FeedState::new(pane_cx);
seed(&feed);
*h.borrow_mut() = Some(feed.clone());
let view = crate::ui::Element::new()
.style(
LayoutStyle::default()
.width(Dimension::Percent(1.0))
.height(Dimension::Percent(1.0)),
)
.child(
Feed::new(&feed)
.on_item_press(move |_, _| pane_cx.dispose())
.view(pane_cx),
)
.build();
tree.mount(pane_cx, view);
});
let _ = settle(&mut tree, size);
click(&mut tree, 2, 0);
assert_eq!(tree.instance_count(), 0, "subtree unmounted by dispose");
root.dispose();
}