Tests: the file-dialog menu test reruns while fonts or plug-ins change

`the_menu_bar_is_inert_while_a_file_dialog_is_open` excused a missing
menu row when the composite font or plug-in it named was gone at the
time of the check. A test running alongside can remove such a font and
define it again between the menu read and the check, and the test then
failed. It now runs inside `undisturbed`, as the file's other tests that
compare menu reads do. When the shortcut, plug-in or font generation
changes during a run, the test runs again on a new app. Every row from
before the dialog must then be back as it was.
This commit is contained in:
dotCooCoo
2026-11-12 05:32:35 -08:00
parent a5d0fe7963
commit f317aefa44
+44 -64
View File
@@ -69,25 +69,6 @@ fn find<'a>(bar: &'a MenuBar, command: &str) -> &'a native_menu::Item {
bar.items().into_iter().find(|it| it.command == Some(command)).unwrap_or_else(|| panic!("no {command} item"))
}
/// Is the font or plug-in that an item runs `command` with `params` for gone from the process-wide
/// font database or plug-in registry? A test running alongside can remove a composite font or a
/// plug-in, and the menus then drop its item.
fn source_gone(command: Option<&str>, params: &Value) -> bool {
use vectorcraft_plugins::{PREFIX, effect, export, registry};
let field = |key| params.get(key).and_then(Value::as_str);
let plugin_gone = |id: Option<&str>| id.is_some_and(|id| registry::get(id).is_none());
match command {
Some("text.setStyle") => field("font").is_some_and(|f| !vectorcraft_text::FontDb::global().menu_family_list_now().iter().any(|x| x == f)),
Some("plugin.dialog") => plugin_gone(field("id")),
Some("file.openWith") => plugin_gone(field("importer").and_then(export::plugin_id)),
Some("effect.apply" | "effect.dialog") => plugin_gone(field("effect").and_then(effect::plugin_id)),
Some(c) if vectorcraft_engine::cmd::plugin::is_command_id(c) => {
plugin_gone(c.strip_prefix(PREFIX).and_then(|rest| rest.rsplit_once('.')).map(|(id, _)| id))
}
_ => false,
}
}
#[test]
fn vectorcraft_gets_a_mac_app_menu() {
let bar = bar(&app(true));
@@ -702,52 +683,51 @@ fn the_app_bar_hides_its_menus_with_the_mac_menu_bar() {
/// answers, the menus are as before and work again.
#[test]
fn the_menu_bar_is_inert_while_a_file_dialog_is_open() {
let mut m = Mac::new(true);
m.app.services.dialogs_are_sheets = true;
let (shown, _) = crate::tests_picks::off_the_ui_thread(&mut m.app.services);
m.app.run("shape.rectangle", json!({"x": 200, "y": 150, "width": 20, "height": 20})).unwrap();
m.app.run("select.none", json!({})).unwrap();
m.frame(vec![]);
let rows = |bar: &MenuBar| bar.items().iter().map(|it| (it.command, it.params.clone(), it.enabled)).collect::<Vec<_>>();
let before = m.fake.last();
let system = [native_menu::HIDE, native_menu::HIDE_OTHERS, native_menu::MINIMIZE];
assert!(system.iter().all(|c| find(&before, c).enabled));
assert!(m.app.run("file.open", json!({})).is_err());
assert!(m.app.file_dialog_open());
m.frame(vec![]);
let inert = m.fake.last();
for it in inert.items() {
let kept = it.command.is_some_and(|c| system.contains(&c));
assert_eq!(it.enabled, kept, "{:?} {} while the dialog is open", it.command, it.label);
}
// A click that raced the menu going inert isn't run; Minimize is.
let rulers = (m.app.ui.view.rulers, find(&before, "view.rulers").clone());
m.fake.report(native_menu::Event::Click(rulers.1.clone()));
m.frame(vec![]);
assert_eq!(m.app.ui.view.rulers, rulers.0);
m.fake.report(native_menu::Event::Click(find(&inert, native_menu::MINIMIZE).clone()));
assert!(m.frame(vec![]).viewport_output[&egui::ViewportId::ROOT].commands.contains(&egui::ViewportCommand::Minimized(true)));
// A key equivalent that raced it doesn't reach egui.
let mut k = crate::shortcuts::parse("Cmd+A").unwrap();
k.modifiers.mac_cmd = k.modifiers.command;
m.fake.report(native_menu::Event::Key(k));
let mut raw = egui::RawInput::default();
m.app.raw_input_hook(&m.ctx, &mut raw);
assert!(!raw.events.iter().any(|e| matches!(e, Event::Key { .. })), "{:?}", raw.events);
m.key_equivalent("Cmd+A");
assert_eq!(m.selected(), 0, "⌘A selected nothing");
// Cancelled: the menus are as before, and work again.
shown.answer(&[]);
m.frame(vec![]);
m.frame(vec![]);
assert!(!m.app.file_dialog_open());
// Every row from before is back as it was. Tests running alongside may add rows, and may
// remove the composite font or plug-in a row is for.
let after = rows(&m.fake.last());
for r in rows(&before) {
let listed = after.iter().any(|a| (a.0, &a.1) == (r.0, &r.1));
assert!(after.contains(&r) || (!listed && source_gone(r.0, &r.1)), "{r:?} is not as before");
}
// The menus read before the dialog and after it are compared, so the whole run is undisturbed:
// a test running alongside can add or remove a composite font or a plug-in in between.
let (mut m, (before, after, rulers)) = undisturbed(|m| {
m.app.services.dialogs_are_sheets = true;
let (shown, _) = crate::tests_picks::off_the_ui_thread(&mut m.app.services);
m.app.run("shape.rectangle", json!({"x": 200, "y": 150, "width": 20, "height": 20})).unwrap();
m.app.run("select.none", json!({})).unwrap();
m.frame(vec![]);
let before = m.fake.last();
let system = [native_menu::HIDE, native_menu::HIDE_OTHERS, native_menu::MINIMIZE];
assert!(system.iter().all(|c| find(&before, c).enabled));
assert!(m.app.run("file.open", json!({})).is_err());
assert!(m.app.file_dialog_open());
m.frame(vec![]);
let inert = m.fake.last();
for it in inert.items() {
let kept = it.command.is_some_and(|c| system.contains(&c));
assert_eq!(it.enabled, kept, "{:?} {} while the dialog is open", it.command, it.label);
}
// A click that raced the menu going inert isn't run; Minimize is.
let rulers = (m.app.ui.view.rulers, find(&before, "view.rulers").clone());
m.fake.report(native_menu::Event::Click(rulers.1.clone()));
m.frame(vec![]);
assert_eq!(m.app.ui.view.rulers, rulers.0);
m.fake.report(native_menu::Event::Click(find(&inert, native_menu::MINIMIZE).clone()));
assert!(m.frame(vec![]).viewport_output[&egui::ViewportId::ROOT].commands.contains(&egui::ViewportCommand::Minimized(true)));
// A key equivalent that raced it doesn't reach egui.
let mut k = crate::shortcuts::parse("Cmd+A").unwrap();
k.modifiers.mac_cmd = k.modifiers.command;
m.fake.report(native_menu::Event::Key(k));
let mut raw = egui::RawInput::default();
m.app.raw_input_hook(&m.ctx, &mut raw);
assert!(!raw.events.iter().any(|e| matches!(e, Event::Key { .. })), "{:?}", raw.events);
m.key_equivalent("Cmd+A");
assert_eq!(m.selected(), 0, "⌘A selected nothing");
// Cancelled: the menus are as before, and work again.
shown.answer(&[]);
m.frame(vec![]);
m.frame(vec![]);
assert!(!m.app.file_dialog_open());
(rows(&before), rows(&m.fake.last()), rulers)
});
// Every row from before is back as it was.
assert!(before.iter().all(|r| after.contains(r)), "the menus are as before");
m.fake.report(native_menu::Event::Click(rulers.1));
m.frame(vec![]);
assert_eq!(m.app.ui.view.rulers, !rulers.0);