test(grid): ensure formulas are recomputed before rendering

The render helper used in Grid tests previously did not recompute formulas,
meaning tests for computed values were either ignored or required manual
setup that didn't reflect actual application behavior.

Update the render helper to identify 'None' axis categories and trigger
recompute_formulas, and update all call sites to pass a mutable model.

Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-31B-it-UD-Q4_K_XL.gguf)
This commit is contained in:
Edward Langley
2026-04-14 00:49:31 -07:00
parent f019577810
commit 8baa4c4865

View File

@ -682,7 +682,14 @@ mod tests {
// ── Helpers ───────────────────────────────────────────────────────────────
/// Render a GridWidget into a fresh buffer of the given size.
fn render(model: &Model, width: u16, height: u16) -> Buffer {
fn render(model: &mut Model, width: u16, height: u16) -> Buffer {
let none_cats: Vec<String> = model
.active_view()
.categories_on(crate::view::Axis::None)
.into_iter()
.map(String::from)
.collect();
model.recompute_formulas(&none_cats);
let area = Rect::new(0, 0, width, height);
let mut buf = Buffer::empty(area);
let bufs = std::collections::HashMap::new();
@ -743,8 +750,8 @@ mod tests {
#[test]
fn column_headers_appear() {
let m = two_cat_model();
let text = buf_text(&render(&m, 80, 24));
let mut m = two_cat_model();
let text = buf_text(&render(&mut m, 80, 24));
assert!(text.contains("Jan"), "expected 'Jan' in:\n{text}");
assert!(text.contains("Feb"), "expected 'Feb' in:\n{text}");
}
@ -753,8 +760,8 @@ mod tests {
#[test]
fn row_headers_appear() {
let m = two_cat_model();
let text = buf_text(&render(&m, 80, 24));
let mut m = two_cat_model();
let text = buf_text(&render(&mut m, 80, 24));
assert!(text.contains("Food"), "expected 'Food' in:\n{text}");
assert!(text.contains("Clothing"), "expected 'Clothing' in:\n{text}");
}
@ -768,7 +775,7 @@ mod tests {
coord(&[("Type", "Food"), ("Month", "Jan")]),
CellValue::Number(123.0),
);
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
assert!(text.contains("123"), "expected '123' in:\n{text}");
}
@ -787,7 +794,7 @@ mod tests {
coord(&[("Type", "Clothing"), ("Month", "Jan")]),
CellValue::Number(50.0),
);
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
assert!(text.contains("100"), "expected '100' in:\n{text}");
assert!(text.contains("200"), "expected '200' in:\n{text}");
assert!(text.contains("50"), "expected '50' in:\n{text}");
@ -806,7 +813,7 @@ mod tests {
coord(&[("Type", "Food"), ("Month", "Jan")]),
CellValue::Number(1.0),
);
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
// Should not contain large numbers that weren't set
assert!(!text.contains("100"), "unexpected '100' in:\n{text}");
}
@ -815,8 +822,8 @@ mod tests {
#[test]
fn total_row_label_appears() {
let m = two_cat_model();
let text = buf_text(&render(&m, 80, 24));
let mut m = two_cat_model();
let text = buf_text(&render(&mut m, 80, 24));
assert!(text.contains("Total"), "expected 'Total' in:\n{text}");
}
@ -831,7 +838,7 @@ mod tests {
coord(&[("Type", "Clothing"), ("Month", "Jan")]),
CellValue::Number(50.0),
);
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
// Food(100) + Clothing(50) = 150 for Jan
assert!(
text.contains("150"),
@ -858,7 +865,7 @@ mod tests {
c.add_item("Bob");
}
m.active_view_mut().set_page_selection("Payer", "Bob");
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
assert!(
text.contains("Payer = Bob"),
"expected 'Payer = Bob' in:\n{text}"
@ -882,7 +889,7 @@ mod tests {
c.add_item("Bob");
}
// No explicit selection — should default to first item
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
assert!(
text.contains("Payer = Alice"),
"expected 'Payer = Alice' in:\n{text}"
@ -892,19 +899,13 @@ mod tests {
// ── Formula evaluation ────────────────────────────────────────────────────
#[test]
#[ignore = "needs render harness update for _Measure virtual category"]
fn formula_cell_renders_computed_value() {
let mut m = Model::new("Test");
m.add_category("_Measure").unwrap(); // → Row
m.add_category("Region").unwrap(); // → Column
if let Some(c) = m.category_mut("_Measure") {
c.add_item("Revenue");
c.add_item("Cost");
c.add_item("Profit");
}
if let Some(c) = m.category_mut("Region") {
c.add_item("East");
}
m.category_mut("_Measure").unwrap().add_item("Revenue");
m.category_mut("_Measure").unwrap().add_item("Cost");
// Profit is a formula target — dynamically included in _Measure
m.category_mut("Region").unwrap().add_item("East");
m.set_cell(
coord(&[("_Measure", "Revenue"), ("Region", "East")]),
CellValue::Number(1000.0),
@ -914,12 +915,16 @@ mod tests {
CellValue::Number(600.0),
);
m.add_formula(parse_formula("Profit = Revenue - Cost", "_Measure").unwrap());
m.active_view_mut()
.set_axis("_Index", crate::view::Axis::None);
m.active_view_mut()
.set_axis("_Dim", crate::view::Axis::None);
m.active_view_mut()
.set_axis("_Measure", crate::view::Axis::Row);
m.active_view_mut()
.set_axis("Region", crate::view::Axis::Column);
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
assert!(text.contains("400"), "expected '400' (Profit) in:\n{text}");
}
@ -954,7 +959,7 @@ mod tests {
}
}
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
// Multi-level row headers: category values shown separately, not joined with /
assert!(
!text.contains("Food/Alice"),
@ -994,7 +999,7 @@ mod tests {
coord(&[("Month", "Jan"), ("Recipient", "Alice"), ("Type", "Food")]),
CellValue::Number(77.0),
);
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
assert!(text.contains("77"), "expected '77' in:\n{text}");
}
@ -1024,7 +1029,7 @@ mod tests {
);
}
let text = buf_text(&render(&m, 80, 24));
let text = buf_text(&render(&mut m, 80, 24));
// Multi-level column headers: category values shown separately, not joined with /
assert!(
!text.contains("Jan/2024"),