diff --git a/crates/ui-egui/tests/keytips_generated.rs b/crates/ui-egui/tests/keytips_generated.rs index 653554857..5f2acdbb0 100644 --- a/crates/ui-egui/tests/keytips_generated.rs +++ b/crates/ui-egui/tests/keytips_generated.rs @@ -1276,7 +1276,7 @@ fn keytip_nq_() { fn keytip_nr_() { let mut h = harness(blank()); sequence(&mut h, "NR", false); - assert!(h.state().message.is_some() || h.state().toast.is_some(), "NR must invoke insert.recommendedCharts and surface its missing-params error"); + assert_eq!(h.state().dialog.as_ref().map(|d| d.name.as_str()), Some("recommendedCharts"), "NR must open the recommendedCharts dialog"); } #[test] diff --git a/xtask/src/keytips.rs b/xtask/src/keytips.rs index 36847222f..e4d423199 100644 --- a/xtask/src/keytips.rs +++ b/xtask/src/keytips.rs @@ -17,6 +17,10 @@ const TABS: &[&str] = &["Home", "Insert", "Draw", "Page Layout", "Formulas", "Da /// something observable" so the checked-in file stays byte-identical everywhere. const ENV_SENSITIVE: &[&str] = &["review.spelling"]; +/// Commands that the UI turns into a dialog when invoked menu-style with no params (the engine +/// command itself errors without them). Never probed; their tests assert the dialog opens. +const UI_DIALOG_COMMANDS: &[(&str, &str)] = &[("insert.recommendedCharts", "recommendedCharts")]; + /// Format generated source through `rustfmt` so the checked-in file matches /// the emitter byte-for-byte. fn rustfmt(src: &str) -> Result { @@ -57,7 +61,7 @@ fn probe_live_commands(ids: &[String]) -> Probe { use gridcraft_engine::Session; let mut map = Probe::new(); for id in ids { - if ENV_SENSITIVE.contains(&id.as_str()) { + if ENV_SENSITIVE.contains(&id.as_str()) || UI_DIALOG_COMMANDS.iter().any(|(c, _)| c == id) { continue; } let mut s = Session::new(); @@ -269,6 +273,13 @@ fn render_tests(entries: &[Entry], probe: &Probe) -> String { "sequence(&mut h, {seq:?}, false);\n assert!(h.state().dialog.is_none(), \"{seq} must not open a dialog\");\n assert!(h.state().message.is_none(), \"{seq} must not open a message box\");\n assert!(h.state().toast.is_none(), \"{seq} must not raise a toast\");\n assert!(h.state().session.journal.is_empty(), \"{seq} must not journal\");", seq = e.seq, ), + "command" if UI_DIALOG_COMMANDS.iter().any(|(c, _)| *c == e.target) => { + let dialog = UI_DIALOG_COMMANDS.iter().find(|(c, _)| *c == e.target).map_or("", |(_, d)| d); + format!( + "sequence(&mut h, {seq:?}, false);\n assert_eq!(h.state().dialog.as_ref().map(|d| d.name.as_str()), Some({dialog:?}), \"{seq} must open the {dialog} dialog\");", + seq = e.seq, + ) + } "command" => match probe.get(&e.target).copied().unwrap_or(2) { 0 => format!( "sequence(&mut h, {seq:?}, false);\n assert!(h.state().session.journal.iter().any(|(id, _)| id == {id:?}), \"{seq} must journal {id}\");",