Files
figcraft/crates/engine/src/text_motion_tests.rs
T
BFlat 10a9b7b165 M8: Windows QA fixes (#131)
Found and fixed in a Windows pass of everyday workflows: unsaved documents are now copied in the background (at most every 10 s) and offered back after a crash or forced quit (file.recover); Ctrl+arrow / Ctrl+Backspace / Ctrl+Delete text motion follows Windows and Linux conventions (keymap::text_motion); number-field scrubbing keeps its value; typing in a Tab-focused field works; default linear gradients and their handles behave; Escape closes dialogs without deselecting; clipped panel hints wrap; the unsaved-changes dialog is translated.
2026-10-11 18:20:38 +07:00

73 lines
3.5 KiB
Rust

//! Text editing keys per platform (Ctrl moves and deletes by word on Windows and Linux, where it
//! deleted the whole line), and recovering a crashed run's copy as an unsaved document.
use serde_json::json;
use crate::keymap::{Mods, Platform, text_motion};
use crate::{Session, params, textedit};
const CTRL: Mods = Mods { shift: false, alt: false, cmd: true, ctrl: false };
const ALT: Mods = Mods { shift: false, alt: true, cmd: false, ctrl: false };
/// Press `key` with `m` as the app does on `platform`; the text afterwards.
fn key(s: &mut Session, platform: Platform, key: &str, m: Mods) -> String {
let (word, line) = text_motion(platform, key, m);
textedit::key(s, key, m.shift, word, line).unwrap();
let id = s.ds().unwrap().text_edit.as_ref().unwrap().id;
s.doc().unwrap().node(id).unwrap().kind.text().unwrap().characters.clone()
}
fn editing(text: &str) -> Session {
let mut s = Session::new();
let t = params::id_of(&s.execute("text.create", &json!({"text": text, "x": 0, "y": 0})).unwrap()["id"]).unwrap();
textedit::begin(&mut s, t, Some(text.chars().count()), false).unwrap();
s
}
fn caret(s: &Session) -> usize {
s.ds().unwrap().text_edit.as_ref().unwrap().caret
}
#[test]
fn ctrl_moves_and_deletes_by_word_on_windows_and_linux() {
let mut s = editing("Hello brave world");
assert_eq!(key(&mut s, Platform::Other, "Backspace", CTRL), "Hello brave ", "Ctrl+Backspace deletes one word");
key(&mut s, Platform::Other, "Left", CTRL);
assert_eq!(caret(&s), 6, "Ctrl+Left goes to the word's start");
// The control channel and MCP name arrows like egui.
key(&mut s, Platform::Other, "ArrowLeft", CTRL);
assert_eq!(caret(&s), 0);
assert_eq!(key(&mut s, Platform::Other, "Delete", CTRL), " brave ", "Ctrl+Delete deletes the next word");
key(&mut s, Platform::Other, "End", CTRL);
assert_eq!(caret(&s), 7, "Ctrl+End: the end of the text");
key(&mut s, Platform::Other, "Home", CTRL);
assert_eq!(caret(&s), 0, "Ctrl+Home: its start");
}
#[test]
fn macos_keeps_alt_by_word_and_cmd_to_the_line_end() {
assert_eq!(text_motion(Platform::Mac, "Left", ALT), (true, false));
assert_eq!(text_motion(Platform::Mac, "Backspace", CTRL), (false, true));
assert_eq!(text_motion(Platform::Other, "Left", ALT), (false, false), "Alt+arrows do nothing special on Windows");
let mut s = editing("Hello brave world");
assert_eq!(key(&mut s, Platform::Mac, "Backspace", ALT), "Hello brave ");
assert_eq!(key(&mut s, Platform::Mac, "Backspace", CTRL), "", "Cmd+Backspace: to the line's start");
}
#[test]
fn a_recovered_copy_opens_unsaved_without_a_path() {
let dir = std::env::temp_dir().join(format!("figcraft-recover-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("1.figcraft");
let mut s = Session::new();
s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap();
s.execute("file.save", &json!({"path": path.to_string_lossy()})).unwrap();
let r = s.execute("file.recover", &json!({"path": path.to_string_lossy()})).unwrap();
let ds = s.docs.get(r["index"].as_u64().unwrap() as usize).unwrap();
assert!(ds.path.is_none(), "saving asks where: the copy isn't the file");
assert!(ds.is_dirty(), "a closed window or tab asks before dropping it");
assert_eq!(s.doc().unwrap().pages.len(), 1);
assert!(s.execute("file.recover", &json!({"path": dir.join("missing.figcraft").to_string_lossy()})).is_err());
let _ = std::fs::remove_dir_all(&dir);
}