improvise-7mro: red lockstep matrix, keys sent before a scroll is answered
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:
@@ -2662,3 +2662,140 @@ fn renaming_a_formula_from_the_panel_lands_the_same_document_as_the_host() {
|
||||
assert_eq!(client.view.status_msg, "Formula updated");
|
||||
assert_eq!(app.view_state.status_msg, "Renamed Tax to Levy");
|
||||
}
|
||||
|
||||
/// One scroll-then-edit case: the window, where the cursor starts, and the keys.
|
||||
type Pipelined = (
|
||||
&'static str,
|
||||
(usize, usize),
|
||||
((usize, usize), usize, usize),
|
||||
&'static [&'static str],
|
||||
);
|
||||
|
||||
/// `wrapping_host`'s cells that differ from how it was built, as `Month/Measure`.
|
||||
fn written_cells(app: &App) -> Vec<String> {
|
||||
let model = &app.model_state.workbook.model;
|
||||
let mut written = Vec::new();
|
||||
for month in ["Jan", "Feb"] {
|
||||
for (measure, built) in [("Revenue", Some(10.0)), ("Profit", None), ("Cost", Some(4.0))] {
|
||||
let key = improvise_command::model::cell::CellKey::new(vec![
|
||||
("Month".to_string(), month.to_string()),
|
||||
("_Measure".to_string(), measure.to_string()),
|
||||
]);
|
||||
let now = model.get_cell(&key).cloned();
|
||||
if now != built.map(improvise_protocol::CellValue::Number) {
|
||||
written.push(format!("{month}/{measure}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
written
|
||||
}
|
||||
|
||||
/// One key on the client, its frame sent to the host; the reply comes back
|
||||
/// for the caller to deliver or hold. An upstream key reaches a host that answers nothing.
|
||||
fn sent(
|
||||
host: &mut improvise_command::ui::host::Host,
|
||||
session: improvise_command::ui::project::SessionId,
|
||||
client: &mut ClientState,
|
||||
key: &str,
|
||||
) -> Vec<improvise_command::ui::host::Addressed> {
|
||||
match resolve(client, BrowserKey::new(key).to_key_event().unwrap()) {
|
||||
KeyOutcome::Local(cmds) => {
|
||||
client.fold_outgoing(&cmds);
|
||||
host.handle(session, ClientFrame::Commands(cmds))
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// improvise-7mro: the offset-aliasing matrix, replies delivered and replies
|
||||
/// held. See `context/test-evidence/clients.md`.
|
||||
#[test]
|
||||
fn keys_sent_before_a_scroll_is_answered_agree_with_the_tui() {
|
||||
let cases: [Pipelined; 5] = [
|
||||
("wrap", (2, 6), ((2, 0), 1, 0), &["i", "Enter", "8", "Enter"]),
|
||||
("horizontal wrap", (2, 1), ((2, 0), 1, 0), &["i", "Enter", "8", "Enter"]),
|
||||
("G", (2, 6), ((0, 0), 0, 0), &["G", "i", "7", "Enter"]),
|
||||
("$", (2, 1), ((0, 0), 0, 0), &["$", "i", "7", "Enter"]),
|
||||
("gg", (2, 6), ((2, 0), 1, 0), &["g", "g", "i", "7", "Enter"]),
|
||||
];
|
||||
for (name, window, (at, row_offset, col_offset), keys) in cases {
|
||||
let mut tui = wrapping_host();
|
||||
tui.cursor_mut().selected = at;
|
||||
for &k in keys {
|
||||
reduce_key(&mut tui, BrowserKey::new(k).to_key_event().unwrap());
|
||||
}
|
||||
assert_eq!(written_cells(&tui).len(), 1, "setup: {name}: the TUI wrote one cell");
|
||||
for held in [false, true] {
|
||||
let what = format!("{name} ({keys:?}, replies held: {held})");
|
||||
let mut host = improvise_command::ui::host::Host::new(wrapping_host());
|
||||
let (session, welcome) = host.connect(window.0, window.1);
|
||||
let mut client = ClientState {
|
||||
window,
|
||||
..ClientState::default()
|
||||
};
|
||||
deliver(vec![welcome], session, &mut client);
|
||||
let placed = vec![
|
||||
improvise_protocol::Command::SetSelected {
|
||||
row: at.0,
|
||||
col: at.1,
|
||||
},
|
||||
improvise_protocol::Command::SetRowOffset(row_offset),
|
||||
improvise_protocol::Command::SetColOffset(col_offset),
|
||||
];
|
||||
client.fold_outgoing(&placed);
|
||||
deliver(
|
||||
host.handle(session, ClientFrame::Commands(placed)),
|
||||
session,
|
||||
&mut client,
|
||||
);
|
||||
let mut replies = Vec::new();
|
||||
for &k in keys {
|
||||
let reply = sent(&mut host, session, &mut client, k);
|
||||
match held {
|
||||
true => replies.extend(reply),
|
||||
false => deliver(reply, session, &mut client),
|
||||
}
|
||||
}
|
||||
if held {
|
||||
assert_eq!(
|
||||
written_cells(host.app()),
|
||||
Vec::<String>::new(),
|
||||
"{what}: a write was composed before its target was projected"
|
||||
);
|
||||
assert!(
|
||||
client.view.mode.is_editing() && !client.view.status_msg.is_empty(),
|
||||
"{what}: Enter must wait out loud with the editor open: {:?}, {:?}",
|
||||
client.view.mode,
|
||||
client.view.status_msg
|
||||
);
|
||||
deliver(replies, session, &mut client);
|
||||
let reply = sent(&mut host, session, &mut client, "Enter");
|
||||
deliver(reply, session, &mut client);
|
||||
}
|
||||
assert_eq!(
|
||||
written_cells(host.app()),
|
||||
written_cells(&tui),
|
||||
"{what}: the browser wrote a different cell from the TUI"
|
||||
);
|
||||
if !held {
|
||||
assert_eq!(document_text(host.app()), document_text(&tui), "{what}");
|
||||
}
|
||||
assert_eq!(
|
||||
client.view.cursor().selected,
|
||||
tui.view_state.cursor().selected,
|
||||
"{what}: the cursor"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", client.view.mode),
|
||||
format!("{:?}", tui.view_state.mode),
|
||||
"{what}: the mode; browser status {:?}",
|
||||
client.view.status_msg
|
||||
);
|
||||
assert_eq!(
|
||||
client.view.buffers.get("edit"),
|
||||
tui.view_state.buffers.get("edit"),
|
||||
"{what}: the editor's buffer"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user