use day_core::AnyPiece;
use day_mock::{MockProbe, MockToolkit};
use day_pieces::prelude::*;
use day_reactive::flush_sync;
use day_spec::{Event, NodeId, WindowOptions};
fn boot(root: impl FnOnce() -> AnyPiece + 'static) -> MockProbe {
day_core::uninstall_tree();
let (mock, probe) = MockToolkit::new();
let options = WindowOptions {
title: "test".into(),
size: Size::new(400.0, 600.0),
..Default::default()
};
day_core::launch_with(mock, options, root);
probe
}
fn label_frames(probe: &MockProbe) -> Vec<Rect> {
probe
.find_by_kind("day.label")
.iter()
.map(|(_, w)| w.frame)
.collect()
}
#[test]
fn grid_columns_size_to_max_cell() {
let probe = boot(|| {
grid((
grid_row((label("aa"), label("bbbb"))),
grid_row((label("cccccc"), label("d"))),
))
.spacing(10.0)
.align(Alignment::TopLeading)
.any()
});
let f = label_frames(&probe);
assert_eq!(f[0], Rect::new(0.0, 0.0, 16.0, 16.0), "{f:?}");
assert_eq!(
f[1],
Rect::new(58.0, 0.0, 32.0, 16.0),
"col1 = col0 + gutter"
);
assert_eq!(
f[2],
Rect::new(0.0, 26.0, 48.0, 16.0),
"row1 = row0 + gutter"
);
assert_eq!(f[3], Rect::new(58.0, 26.0, 8.0, 16.0), "{f:?}");
}
#[test]
fn grid_spacer_is_empty_cell() {
let probe = boot(|| {
grid((
grid_row((spacer(), label("bb"))),
grid_row((label("aaaa"), label("cc"))),
))
.spacing(10.0)
.align(Alignment::TopLeading)
.any()
});
let f = label_frames(&probe);
assert_eq!(f[0], Rect::new(42.0, 0.0, 16.0, 16.0), "{f:?}");
assert_eq!(f[1], Rect::new(0.0, 26.0, 32.0, 16.0), "{f:?}");
assert_eq!(f[2], Rect::new(42.0, 26.0, 16.0, 16.0), "{f:?}");
}
#[test]
fn grid_flexible_column_shares_leftover() {
let probe = boot(|| {
grid((
grid_row((label("aaaa"), rectangle().fill(Color::WHITE))),
grid_row((label("bb"), label("cc"))),
))
.align(Alignment::TopLeading)
.height(32.0) .any()
});
let canvas = &probe.find_by_kind("day.canvas")[0].1;
assert_eq!(canvas.frame, Rect::new(32.0, 0.0, 368.0, 16.0));
let f = label_frames(&probe);
assert_eq!(f[2], Rect::new(32.0, 16.0, 16.0, 16.0), "{f:?}");
}
#[test]
fn grid_span_and_full_width() {
let probe = boot(|| {
grid((
grid_row((label("aaaa"), label("bb"))),
grid_row((label("cccccccccc").grid_span(2),)),
label("dddd"), ))
.column_spacing(10.0)
.align(Alignment::TopLeading)
.any()
});
let f = label_frames(&probe);
assert_eq!(f[0], Rect::new(0.0, 0.0, 32.0, 16.0), "{f:?}");
assert_eq!(f[1], Rect::new(53.0, 0.0, 16.0, 16.0), "col1 x = 43 + 10");
assert_eq!(f[2], Rect::new(0.0, 16.0, 80.0, 16.0), "span cell");
assert_eq!(f[3], Rect::new(0.0, 32.0, 32.0, 16.0), "full-width cell");
}
#[test]
fn grid_alignment_precedence() {
let probe = boot(|| {
grid((
grid_row((
rectangle().fill(Color::WHITE).frame(20.0, 40.0),
label("aa"),
)),
grid_row((
rectangle().fill(Color::WHITE).frame(20.0, 40.0),
label("aa"),
))
.align(VAlign::Bottom),
grid_row((
rectangle().fill(Color::WHITE).frame(20.0, 40.0),
label("aa").grid_align(Alignment::BottomTrailing),
)),
grid_row((spacer(), label("bbbb"))),
))
.align(Alignment::TopLeading)
.any()
});
let f = label_frames(&probe);
assert_eq!(f[0], Rect::new(20.0, 0.0, 16.0, 16.0), "{f:?}");
assert_eq!(
f[1],
Rect::new(20.0, 64.0, 16.0, 16.0),
"row valign: y = 40 + (40 − 16)"
);
assert_eq!(
f[2],
Rect::new(36.0, 104.0, 16.0, 16.0),
"cell align: x = 20 + (32 − 16), y = 80 + (40 − 16)"
);
assert_eq!(f[3], Rect::new(20.0, 120.0, 32.0, 16.0), "{f:?}");
}
#[test]
fn grid_height_for_width_rewrap() {
let probe = boot(|| {
grid((grid_row((
label("aa"),
label("abcdefghijklmnopqrstuvwxyz1234").grow_w(),
)),))
.align(Alignment::TopLeading)
.width(100.0)
.any()
});
let f = label_frames(&probe);
assert_eq!(f[1].origin.x, 16.0, "{f:?}");
assert_eq!(f[1].size, Size::new(84.0, 48.0), "3 wrapped lines: {f:?}");
}
#[test]
fn grid_each_rows_reflow() {
let items = Signal::new(vec!["aa".to_string(), "bb".to_string()]);
let probe = boot(move || {
grid((each(
day_pieces::items(move || items.get(), |s: &String| s.clone()),
|slot| grid_row((label(move || slot.get()), label("x"))).any(),
),))
.column_spacing(10.0)
.align(Alignment::TopLeading)
.any()
});
let xs: Vec<f64> = label_frames(&probe).iter().map(|f| f.origin.x).collect();
assert_eq!(xs, vec![0.0, 26.0, 0.0, 26.0], "{xs:?}");
batch(|| items.update(|v| v.push("cccc".to_string())));
flush_sync();
let f = label_frames(&probe);
let xs: Vec<f64> = f.iter().map(|f| f.origin.x).collect();
assert_eq!(
xs,
vec![0.0, 42.0, 0.0, 42.0, 0.0, 42.0],
"col0 must widen to 32 for all rows: {f:?}"
);
}
#[test]
fn grid_in_scroll_reports_content() {
let probe = boot(|| {
let rows: Vec<AnyPiece> = (0..40)
.map(|r| grid_row((label(format!("row {r}")), label("x"))).any())
.collect();
scroll(grid(rows).spacing(10.0)).any()
});
let scrolls = probe.find_by_kind("day.scroll");
assert_eq!(scrolls[0].1.scroll_content.height, 1030.0);
}
#[test]
fn grid_rtl_mirrors_columns() {
day_core::set_layout_direction(day_geometry::LayoutDirection::Rtl);
let probe = boot(|| {
grid((grid_row((label("aa"), rectangle().fill(Color::WHITE))),))
.align(Alignment::TopLeading)
.frame(400.0, 16.0)
.any()
});
let f = label_frames(&probe);
assert_eq!(f[0], Rect::new(384.0, 0.0, 16.0, 16.0), "{f:?}");
let canvas = &probe.find_by_kind("day.canvas")[0].1;
assert_eq!(canvas.frame, Rect::new(0.0, 0.0, 384.0, 16.0));
}
#[test]
fn grid_measure_calls_bounded() {
const ROWS: usize = 100;
const COLS: usize = 6;
let hot = Signal::new("aaaa".to_string());
let probe = boot(move || {
let rows: Vec<AnyPiece> = (0..ROWS)
.map(|r| {
let mut cells: Vec<AnyPiece> = Vec::with_capacity(COLS);
for c in 0..COLS {
if r == 0 && c == 0 {
cells.push(label(move || hot.get()).any());
} else {
cells.push(label(format!("r{r}c{c}")).any());
}
}
grid_row(cells).any()
})
.collect();
column((
button("+").action(move || hot.set("bbbb".to_string())),
grid(rows).spacing(2.0),
))
.any()
});
let cells = ROWS * COLS;
assert!(
probe.measure_calls() <= 2 * cells + 60,
"boot measured {} times for {} cells",
probe.measure_calls(),
cells
);
probe.clear_log(); let btn = probe.find_by_kind("day.button")[0].1.node;
probe.emit(NodeId(btn), Event::Pressed);
let muts: Vec<String> = probe
.mutations()
.into_iter()
.filter(|m| !m.starts_with("a11y"))
.collect();
assert_eq!(muts.len(), 1, "expected one mutation, got: {muts:?}");
assert!(
probe.measure_calls() <= 6,
"single-cell update re-measured {} times",
probe.measure_calls()
);
}