From 0458b435199a947a913cef885209663a411b3b58 Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Wed, 23 Sep 2026 20:04:29 -0700 Subject: [PATCH] improvise-3h9w: red test, a captured edit is refused for the cursor's aggregate Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze --- .../tests/browser_round_trip.rs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/crates/improvise-wasm-client/tests/browser_round_trip.rs b/crates/improvise-wasm-client/tests/browser_round_trip.rs index 40a080ab..e8db507b 100644 --- a/crates/improvise-wasm-client/tests/browser_round_trip.rs +++ b/crates/improvise-wasm-client/tests/browser_round_trip.rs @@ -1925,3 +1925,76 @@ fn a_retry_after_a_crossing_write_to_its_own_record_is_a_force() { ); assert_eq!(region(&host, "North"), None); } + +/// `host()` with `Profit = Revenue - Cost`, so a grid with Region on None +/// shows aggregates and a derived column, and no stored cell. +fn host_with_profit() -> Host { + let mut wb = Workbook::new("Test"); + wb.add_category("Region").unwrap(); + wb.add_item("Region", "North").unwrap(); + wb.add_item("Region", "South").unwrap(); + wb.add_item("_Measure", "Revenue").unwrap(); + wb.add_item("_Measure", "Cost").unwrap(); + wb.model.add_formula( + improvise_command::formula::parse_formula("Profit = Revenue - Cost", "_Measure").unwrap(), + ); + wb.active_view_mut().set_axis("Region", Axis::Row); + wb.active_view_mut().set_axis("_Measure", Axis::Column); + wb.model.set_cell( + CellKey::new(vec![ + ("Region".into(), "North".into()), + ("_Measure".into(), "Revenue".into()), + ]), + CellValue::Number(1200.0), + ); + let mut app = App::new(wb, None); + app.rebuild_layout(); + Host::new(app) +} + +/// improvise-3h9w: B's reframe left A's cursor on an aggregate, and Enter was +/// refused for THAT cell instead of writing the captured one. Evidence file. +#[test] +fn a_captured_edit_commits_when_the_cursor_now_sits_on_an_aggregate() { + use improvise_command::ui::reduce::caught_up; + use improvise_protocol::ModelCommand; + + let mut host = host_with_profit(); + let (mut a, mut b) = (Client::new(), Client::new()); + let (a_id, a_snap) = host.connect(10, 10); + let (b_id, b_snap) = host.connect(10, 10); + a.set_viewport(10, 10); + b.set_viewport(10, 10); + deliver(&mut a, &[a_snap]); + deliver(&mut b, &[b_snap]); + for key in ["l", "i", "4", "2"] { + press(&mut host, a_id, &mut a, key); + } + let region_off = ModelCommand::SetAxis { + view: host.app().view_name().to_string(), + category: "Region".into(), + axis: Axis::None, + }; + let relayed = host.handle( + b_id, + ClientFrame::command(caught_up(host.app(), region_off)), + ); + route(&relayed, (a_id, &mut a), (b_id, &mut b)); + assert!(a.state().view.mode.is_editing(), "setup: still editing"); + let (row, col) = a.state().view.cursor().selected; + assert!( + matches!( + a.state().grid.cell(row, col).and_then(|c| c.id.clone()), + Some(improvise_protocol::CellId::Aggregate(_)) + ), + "setup: the cursor now sits on an aggregate" + ); + + press(&mut host, a_id, &mut a, "Enter"); + assert_eq!( + cell(&host, "North", "Cost"), + Some(CellValue::Number(42.0)), + "the captured cell must be written: status {:?}", + a.state().view.status_msg + ); +}