diff --git a/src/ui/grid.rs b/src/ui/grid.rs index 198c227..cdfe70a 100644 --- a/src/ui/grid.rs +++ b/src/ui/grid.rs @@ -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 = 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"),