WIP: lockstep yy/p cases where edit text and value disagree (failing)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
@@ -1581,3 +1581,159 @@ fn opens_an_editor(client: &ClientState, key: KeyEvent) -> bool {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// Yank and paste (improvise-dsxq).
|
||||
|
||||
fn region_measure(region: &str, measure: &str) -> improvise_command::model::cell::CellKey {
|
||||
improvise_command::model::cell::CellKey::new(vec![
|
||||
("Region".to_string(), region.to_string()),
|
||||
("_Measure".to_string(), measure.to_string()),
|
||||
])
|
||||
}
|
||||
|
||||
/// Four rows and four measures, the last a formula, holding values on which the
|
||||
/// edit text and the value disagree. Evidence file.
|
||||
fn yank_host() -> App {
|
||||
use improvise_protocol::CellValue::{Number, Text};
|
||||
let mut wb = Workbook::new("Test");
|
||||
wb.add_category("Region").unwrap();
|
||||
for item in ["West", "North", "South", "East"] {
|
||||
wb.add_item("Region", item).unwrap();
|
||||
}
|
||||
for item in ["Units", "Revenue", "Cost"] {
|
||||
wb.add_item("_Measure", item).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);
|
||||
for (region, measure, value) in [
|
||||
("North", "Revenue", Number(1.0)),
|
||||
("North", "Cost", Number(2.0)),
|
||||
("South", "Revenue", Number(1.23456789)),
|
||||
("South", "Cost", Text("007".into())),
|
||||
("East", "Revenue", Number(10.123456)),
|
||||
("East", "Cost", Number(0.5)),
|
||||
] {
|
||||
wb.model.set_cell(region_measure(region, measure), value);
|
||||
}
|
||||
let mut app = App::new(wb, None);
|
||||
app.term_height = 4 + 8;
|
||||
app.rebuild_layout();
|
||||
app
|
||||
}
|
||||
|
||||
/// One unmodified character on both ends: the host through its keymap, a twin
|
||||
/// through exactly what the client composed.
|
||||
fn press(app: &mut App, twin: &mut App, client: &mut ClientState, key: char) {
|
||||
let event = KeyEvent::new(KeyCode::Char(key), KeyModifiers::NONE);
|
||||
let KeyOutcome::Local(cmds) = resolve(client, event) else {
|
||||
panic!("{key}: the client must answer this key locally");
|
||||
};
|
||||
for cmd in &cmds {
|
||||
client.fold(cmd);
|
||||
}
|
||||
apply_on(twin, &cmds);
|
||||
reduce_key(app, event);
|
||||
}
|
||||
|
||||
fn seat(app: &mut App, client: &mut ClientState, at: (usize, usize)) {
|
||||
app.cursor_mut().selected = at;
|
||||
client.view.cursor_mut().selected = at;
|
||||
}
|
||||
|
||||
/// `yy` then `p` copies the value the TUI copies, not its edit text re-parsed.
|
||||
/// Never an integer: the one kind on which the two agree. Evidence file.
|
||||
#[test]
|
||||
fn yy_then_p_copies_the_value_the_host_copies() {
|
||||
use improvise_protocol::CellValue::{Number, Text};
|
||||
let cases = [
|
||||
("a non-whole number", (2, 1), (1, 1), "Revenue", Number(1.23456789)),
|
||||
("number-like text", (2, 2), (1, 2), "Cost", Text("007".into())),
|
||||
("a formula's value", (3, 3), (1, 1), "Revenue", Number(10.123456 - 0.5)),
|
||||
];
|
||||
for (what, from, to, measure, expected) in cases {
|
||||
let mut app = yank_host();
|
||||
let mut twin = yank_host();
|
||||
let mut client = client_ready_to_edit(&app, (4, 4));
|
||||
|
||||
seat(&mut app, &mut client, from);
|
||||
press(&mut app, &mut twin, &mut client, 'y');
|
||||
press(&mut app, &mut twin, &mut client, 'y');
|
||||
assert_eq!(
|
||||
app.view_state.yanked,
|
||||
Some(expected.clone()),
|
||||
"{what}: the host must yank the value itself, or the fixture proves nothing"
|
||||
);
|
||||
assert_eq!(
|
||||
client.view.yanked, app.view_state.yanked,
|
||||
"{what}: client and host yanked different values"
|
||||
);
|
||||
|
||||
seat(&mut app, &mut client, to);
|
||||
press(&mut app, &mut twin, &mut client, 'p');
|
||||
assert_eq!(
|
||||
app.model_state
|
||||
.workbook
|
||||
.model
|
||||
.get_cell(®ion_measure("North", measure)),
|
||||
Some(&expected),
|
||||
"{what}: the host's paste must have landed"
|
||||
);
|
||||
assert_eq!(
|
||||
document_text(&twin),
|
||||
document_text(&app),
|
||||
"{what}: the client's paste wrote a different document"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A true aggregate: `Product` on no axis, so the cell sums two records and has
|
||||
/// no stored value. Yank only: an all-aggregate grid has nowhere to paste.
|
||||
#[test]
|
||||
fn yy_over_an_aggregate_copies_the_sum_the_host_copies() {
|
||||
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_category("Product").unwrap();
|
||||
wb.add_item("Product", "Widget").unwrap();
|
||||
wb.add_item("Product", "Gadget").unwrap();
|
||||
wb.add_item("_Measure", "Revenue").unwrap();
|
||||
wb.active_view_mut().set_axis("Region", Axis::Row);
|
||||
wb.active_view_mut().set_axis("_Measure", Axis::Column);
|
||||
wb.active_view_mut().set_axis("Product", Axis::None);
|
||||
for (product, value) in [("Widget", 1.23456789), ("Gadget", 1.0)] {
|
||||
wb.model.set_cell(
|
||||
region_measure("South", "Revenue").with("Product", product),
|
||||
improvise_protocol::CellValue::Number(value),
|
||||
);
|
||||
}
|
||||
let mut app = App::new(wb, None);
|
||||
app.term_height = 2 + 8;
|
||||
app.rebuild_layout();
|
||||
let mut client = client_of(&app, (2, 1));
|
||||
|
||||
app.cursor_mut().selected = (1, 0);
|
||||
client.view.cursor_mut().selected = (1, 0);
|
||||
for _ in 0..2 {
|
||||
let event = KeyEvent::new(KeyCode::Char('y'), KeyModifiers::NONE);
|
||||
let KeyOutcome::Local(cmds) = resolve(&client, event) else {
|
||||
panic!("y must resolve locally over an aggregate too");
|
||||
};
|
||||
for cmd in &cmds {
|
||||
client.fold(cmd);
|
||||
}
|
||||
reduce_key(&mut app, event);
|
||||
}
|
||||
assert_eq!(
|
||||
app.view_state.yanked,
|
||||
Some(improvise_protocol::CellValue::Number(1.23456789 + 1.0)),
|
||||
"the host yanks the sum, or the fixture is not an aggregate"
|
||||
);
|
||||
assert_eq!(
|
||||
client.view.yanked, app.view_state.yanked,
|
||||
"client and host yanked different values over an aggregate"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user