mirror of
https://github.com/storytold/soundcraft.git
synced 2026-10-26 11:36:51 -06:00
Picking a session with Open Session or dropping one on the page in the browser replaced the document as soon as its bytes were read, without the Save Changes question the native Open goes through, so unsaved edits and their undo history were lost. The bytes now wait behind that question, asked when the read finishes: Don't Save opens them, Save saves first and then opens them, Cancel drops them and leaves the document alone.
154 lines
5.5 KiB
Rust
154 lines
5.5 KiB
Rust
use egui_kittest::Harness;
|
|
use egui_kittest::kittest::Queryable;
|
|
use serde_json::json;
|
|
use soundcraft_ui_egui::dialogs::Dialog;
|
|
use soundcraft_ui_egui::{Services, SoundApp, dialogs, menus};
|
|
|
|
fn harness() -> Harness<'static, SoundApp> {
|
|
let app = SoundApp::new(soundcraft_engine::demo::demo_engine(), None, Services::default());
|
|
Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_ui_state(
|
|
|ui, app: &mut SoundApp| {
|
|
let ctx = ui.ctx().clone();
|
|
app.logic(&ctx);
|
|
app.ui(ui);
|
|
},
|
|
app,
|
|
)
|
|
}
|
|
|
|
fn steps(h: &mut Harness<'_, SoundApp>) {
|
|
for _ in 0..4 {
|
|
h.step();
|
|
}
|
|
}
|
|
|
|
fn click(h: &mut Harness<'_, SoundApp>, label: &str) {
|
|
h.get_by_label(label).click();
|
|
steps(h);
|
|
}
|
|
|
|
fn close_session(h: &mut Harness<'_, SoundApp>) {
|
|
menus::invoke_menu(h.state_mut(), "session.close", "File > Close Session");
|
|
steps(h);
|
|
}
|
|
|
|
fn mute_kick(h: &mut Harness<'_, SoundApp>) {
|
|
h.state_mut().run("mix.mute", json!({"track": "Kick"})).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn closing_a_modified_session_asks_first() {
|
|
let mut h = harness();
|
|
let tracks = h.state().engine.session().tracks.len();
|
|
h.state_mut().run("edit.select", json!({"tracks": ["Kick"], "start": 0, "end": 48_000})).unwrap();
|
|
close_session(&mut h);
|
|
assert!(h.state().dialogs.open.is_none(), "a selection alone is not a change");
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
|
|
let mut h = harness();
|
|
mute_kick(&mut h);
|
|
close_session(&mut h);
|
|
assert_eq!(h.state().dialogs.open_name(), Some("save_changes"));
|
|
click(&mut h, "Cancel");
|
|
assert!(h.state().dialogs.open.is_none());
|
|
assert_eq!(h.state().engine.session().tracks.len(), tracks);
|
|
|
|
close_session(&mut h);
|
|
click(&mut h, "Don't Save");
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
|
|
let mut h = harness();
|
|
mute_kick(&mut h);
|
|
menus::invoke_menu(h.state_mut(), "app.quit", "");
|
|
steps(&mut h);
|
|
assert!(!h.state().quit_requested);
|
|
click(&mut h, "Don't Save");
|
|
assert!(h.state().quit_requested);
|
|
}
|
|
|
|
#[test]
|
|
fn save_in_the_question_saves_before_closing() {
|
|
let dir = std::env::temp_dir().join(format!("soundcraft-save-changes-{}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
let path = dir.join("Song.scraft").to_string_lossy().into_owned();
|
|
|
|
let mut h = harness();
|
|
mute_kick(&mut h);
|
|
close_session(&mut h);
|
|
click(&mut h, "Save");
|
|
match &mut h.state_mut().dialogs.open {
|
|
Some(Dialog::PathPrompt { cmd, path: p, .. }) if cmd == "session.save_as" => p.clone_from(&path),
|
|
other => panic!("expected Save As, got {other:?}"),
|
|
}
|
|
steps(&mut h);
|
|
click(&mut h, "OK");
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
|
|
h.state_mut().run("session.open", json!({"path": path})).unwrap();
|
|
assert!(h.state().engine.session().track_by_name("Kick").unwrap().mixer.mute);
|
|
h.state_mut().run("mix.mute", json!({"track": "Kick"})).unwrap();
|
|
close_session(&mut h);
|
|
click(&mut h, "Save");
|
|
assert!(h.state().dialogs.open.is_none());
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
h.state_mut().run("session.open", json!({"path": path})).unwrap();
|
|
assert!(!h.state().engine.session().track_by_name("Kick").unwrap().mixer.mute);
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|
|
|
|
/// A session the browser read: an empty one, so opening it is easy to see.
|
|
fn session_bytes() -> Vec<u8> {
|
|
soundcraft_engine::Engine::default().session().to_json().unwrap().into_bytes()
|
|
}
|
|
|
|
fn open_bytes(h: &mut Harness<'_, SoundApp>) {
|
|
dialogs::open_session_bytes(h.state_mut(), "Other.scraft".into(), session_bytes());
|
|
steps(h);
|
|
}
|
|
|
|
#[test]
|
|
fn a_session_read_in_the_browser_asks_before_replacing_a_modified_one() {
|
|
let mut h = harness();
|
|
open_bytes(&mut h);
|
|
assert!(h.state().dialogs.open.is_none(), "an unmodified session is replaced without asking");
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
|
|
let mut h = harness();
|
|
let tracks = h.state().engine.session().tracks.len();
|
|
mute_kick(&mut h);
|
|
open_bytes(&mut h);
|
|
assert_eq!(h.state().dialogs.open_name(), Some("save_changes"));
|
|
click(&mut h, "Cancel");
|
|
assert!(h.state().dialogs.open.is_none());
|
|
assert_eq!(h.state().engine.session().tracks.len(), tracks);
|
|
h.state_mut().run("edit.undo", json!({})).unwrap();
|
|
assert!(!h.state().engine.session().track_by_name("Kick").unwrap().mixer.mute, "Cancel keeps the undo history");
|
|
|
|
mute_kick(&mut h);
|
|
open_bytes(&mut h);
|
|
click(&mut h, "Don't Save");
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn save_in_the_question_saves_before_opening_a_session_read_in_the_browser() {
|
|
let dir = std::env::temp_dir().join(format!("soundcraft-save-changes-bytes-{}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
let path = dir.join("Song.scraft").to_string_lossy().into_owned();
|
|
|
|
let mut h = harness();
|
|
mute_kick(&mut h);
|
|
open_bytes(&mut h);
|
|
click(&mut h, "Save");
|
|
match &mut h.state_mut().dialogs.open {
|
|
Some(Dialog::PathPrompt { cmd, path: p, .. }) if cmd == "session.save_as" => p.clone_from(&path),
|
|
other => panic!("expected Save As, got {other:?}"),
|
|
}
|
|
steps(&mut h);
|
|
click(&mut h, "OK");
|
|
assert!(h.state().engine.session().tracks.is_empty());
|
|
h.state_mut().run("session.open", json!({"path": path})).unwrap();
|
|
assert!(h.state().engine.session().track_by_name("Kick").unwrap().mixer.mute);
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|