From bca9a10f8a589c14e15363077fc53dcd5b4ebbab Mon Sep 17 00:00:00 2001 From: prxnvt <53838599+prxnvt@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:04:15 -0400 Subject: [PATCH] UI: saving a new macro never replaces one with the same name A new draft's name can be edited to that of a macro already in the workbook. Save, and Check and Run (which save first), then sent macro.save with its default replace: true and the draft took the other macro's place. A new draft now saves with replace: false, so the clash is reported and the existing macro stays as it was. --- crates/ui-egui/src/macro_editor.rs | 4 +++- crates/ui-egui/tests/it/macro_editor.rs | 29 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/macro_editor.rs b/crates/ui-egui/src/macro_editor.rs index 69e7463a6..c5c30e85b 100644 --- a/crates/ui-egui/src/macro_editor.rs +++ b/crates/ui-egui/src/macro_editor.rs @@ -522,7 +522,9 @@ pub fn save(app: &mut SheetApp, ed: &mut Editor) -> bool { app.message = Some(("Save Macro".into(), crate::clean_error(&e))); return false; } - match app.run("macro.save", json!({"macro": gridcraft_engine::macros::macro_json(&ed.draft)})) { + // A new draft never replaces a macro that has its name. + let replace = ed.saved_as.is_some(); + match app.run("macro.save", json!({"macro": gridcraft_engine::macros::macro_json(&ed.draft), "replace": replace})) { Ok(_) => { ed.saved_as = Some(ed.draft.name.clone()); ed.dirty = false; diff --git a/crates/ui-egui/tests/it/macro_editor.rs b/crates/ui-egui/tests/it/macro_editor.rs index 3f9cda896..8effcfd09 100644 --- a/crates/ui-egui/tests/it/macro_editor.rs +++ b/crates/ui-egui/tests/it/macro_editor.rs @@ -213,3 +213,32 @@ fn run_from_the_editor_asks_before_writing_over_data() { click(&mut h, "Overwrite"); assert_eq!(value(&h, "A2"), Value::Text("new".into())); } + +#[test] +fn a_new_macro_never_replaces_one_with_its_name() { + let mut h = harness(); + three(&mut h); + h.state_mut().macros.editor = None; + h.state_mut().dialog = None; + h.run_steps(2); + click(&mut h, "New Macro"); + for button in ["Save", "Check", "Run"] { + { + let ed = h.state_mut().macros.editor.as_mut().unwrap(); + ed.draft.name = "three".into(); + ed.draft.steps = vec![gridcraft_engine::model::Step::command("cell.set", json!({"cell": "B1", "input": "x"}))]; + ed.dirty = true; + } + h.run_steps(2); + click(&mut h, button); + let wb = &h.state().session.active().unwrap().wb; + assert_eq!(wb.macros.len(), 1, "{button}"); + assert_eq!(wb.macros[0].name, "Three", "{button}"); + assert_eq!(wb.macros[0].steps.len(), 3, "{button}: the saved macro is intact"); + assert!(value(&h, "B1").is_empty(), "{button}: nothing ran"); + let msg = h.state_mut().message.take().map(|(_, m)| m).unwrap_or_default(); + assert!(msg.contains("A macro called 'three' already exists"), "{button}: {msg}"); + assert!(h.state().macros.editor.as_ref().unwrap().saved_as.is_none()); + h.run_steps(2); + } +}