mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-12 21:56:24 +00:00
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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user