Files
2026-10-14 15:28:51 -03:00

116 lines
5.7 KiB
Rust

//! Vim preferences and the engine toggle command (SPEC 6 item 29).
mod common;
use common::{TempVault, temp_config, temp_vault};
use notescraft_engine::{AppSettings, Error, Registry, Session, load_at, register_core, save_at};
use serde_json::{Value, json};
/// An isolated registry, read-only vault and application settings folder.
fn fixture(name: &str) -> (Registry<Session>, Session, TempVault, TempVault) {
let vault = temp_vault(name);
let config = temp_config(name);
let mut registry = Registry::new();
register_core(&mut registry).expect("register");
let mut session = Session::open_read_only(vault.path()).expect("open read-only");
session.use_settings_dir(config.path());
(registry, session, vault, config)
}
#[test]
fn vim_defaults_off_in_new_and_older_documents() {
for document in [json!({}), json!({ "editor": {} })] {
let mut settings: AppSettings = serde_json::from_value(document).expect("missing keys use defaults");
assert_eq!(settings.validate(), Vec::<String>::new());
let value = serde_json::to_value(settings).expect("serialize settings");
assert_eq!(value.pointer("/editor/vim"), Some(&json!(false)));
}
}
#[test]
fn vim_accepts_only_boolean_values() {
for value in [json!("true"), json!(1), Value::Null, json!([]), json!({})] {
assert!(serde_json::from_value::<AppSettings>(json!({ "editor": { "vim": value } })).is_err(), "{value}");
}
}
#[test]
fn vim_survives_toml_save_and_reload() {
let config = temp_config("vim-round-trip");
let mut before: AppSettings = serde_json::from_value(json!({ "editor": { "vim": true } })).expect("boolean preference");
assert_eq!(before.validate(), Vec::<String>::new());
save_at(config.path(), &before).expect("save");
let (after, notes) = load_at(config.path());
assert!(notes.is_empty(), "{notes:?}");
assert_eq!(after, before);
let value = serde_json::to_value(after).expect("serialize settings");
assert_eq!(value.pointer("/editor/vim"), Some(&json!(true)));
}
#[test]
fn invalid_editor_section_recovers_without_losing_other_settings() {
let config = temp_config("vim-invalid-file");
std::fs::write(config.path().join("config.toml"), "schema_version = 1\n\n[editor]\nvim = \"true\"\n\n[session]\ntheme = \"latte\"\n")
.expect("write malformed editor section");
let (settings, notes) = load_at(config.path());
let value = serde_json::to_value(&settings).expect("serialize settings");
assert_eq!(value.pointer("/editor/vim"), Some(&json!(false)));
assert_eq!(settings.session.theme, "latte");
assert!(notes.iter().any(|note| note.starts_with("the `editor` section was replaced by its defaults")), "{notes:?}");
}
#[test]
fn settings_commands_set_validate_and_reset_vim() {
let (registry, mut session, _vault, config) = fixture("vim-settings-commands");
registry.run("settings.set", &mut session, &json!({ "key": "editor.vim", "value": true })).expect("enable Vim");
let settings = registry.run("settings.get", &mut session, &Value::Null).expect("get settings");
assert_eq!(settings.pointer("/settings/editor/vim"), Some(&json!(true)));
for (key, value, code) in [("editor.vim", json!("true"), "invalid_value"), ("editor.unknown", json!(true), "unknown_key")] {
let result = registry.run("settings.set", &mut session, &json!({ "key": key, "value": value }));
assert!(matches!(result, Err(Error::Command { ref source, .. }) if source.code == code), "{result:?}");
}
let (loaded, _) = load_at(config.path());
assert_eq!(serde_json::to_value(loaded).expect("serialize settings").pointer("/editor/vim"), Some(&json!(true)));
registry.run("settings.reset", &mut session, &json!({ "key": "editor.vim" })).expect("reset Vim");
let settings = registry.run("settings.get", &mut session, &Value::Null).expect("get settings");
assert_eq!(settings.pointer("/settings/editor/vim"), Some(&json!(false)));
}
#[test]
fn toggle_vim_persists_each_direction_even_with_read_only_vault() {
let (registry, mut session, vault, config) = fixture("vim-toggle");
assert!(registry.is_enabled("editor.toggle_vim", &session));
registry.run("settings.set", &mut session, &json!({ "key": "session.theme", "value": "latte" })).expect("preserve unrelated setting");
for enabled in [true, false] {
let result = registry.run("editor.toggle_vim", &mut session, &Value::Null).expect("toggle Vim");
assert_eq!(result.get("vim"), Some(&json!(enabled)));
let (settings, notes) = load_at(config.path());
assert!(notes.is_empty(), "{notes:?}");
assert_eq!(settings.session.theme, "latte");
assert_eq!(serde_json::to_value(settings).expect("serialize settings").pointer("/editor/vim"), Some(&json!(enabled)));
}
assert!(!vault.path().join("config.toml").exists());
}
#[test]
fn toggle_vim_is_listed_in_the_edit_menu() {
let (registry, session, _vault, _config) = fixture("vim-toggle-menu");
let commands = registry.list(&session);
let command = commands.iter().find(|command| command.id == "editor.toggle_vim").expect("Vim command metadata");
assert_eq!(command.label, "Vim mode");
assert_eq!(command.menu_path, vec!["Edit"]);
}
#[test]
fn toggle_vim_refuses_to_overwrite_a_newer_schema() {
let (registry, mut session, _vault, config) = fixture("vim-toggle-newer-schema");
let original = "schema_version = 2\n\n[editor]\nvim = false\n";
let path = config.path().join("config.toml");
std::fs::write(&path, original).expect("write newer schema");
let result = registry.run("editor.toggle_vim", &mut session, &Value::Null);
assert!(matches!(result, Err(Error::Command { ref source, .. }) if source.code == "settings"), "{result:?}");
assert_eq!(std::fs::read_to_string(path).expect("read unchanged settings"), original);
}