Files
deckcraft/crates/engine/src/cmd/ruler_tests.rs
T
Brandon ThomasandClaude Opus 5.5 7acb9faad1 Fix up merged PRs: one format.tabs and one Tabs dialog for #567/#569 and #637
#569 and #637 each added a `format.tabs` command and a Tabs dialog. Keep one of each:
- format.tabs takes #567/#569's add/move/remove/clear/set plus #637's strict `set`
  (unknown alignment or non-object entries are errors, stops within 0.05 pt collapse,
  last wins) and opens the Tabs dialog when called with no fields, as #637 did.
  A defaultTab-only call no longer rewrites the stops.
- The Tabs dialog is #569's; it now seeds from the dialog's params when Paragraph (or
  format.tabs) opened it, and on OK hands the applied stops back to Paragraph.
- Tests adapted: an empty format.tabs opens the dialog; the Paragraph → Tabs test sets
  stops through #569's dialog fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:38:03 -04:00

65 lines
3.2 KiB
Rust

//! The ruler's data and tab-stop editing.
use serde_json::json;
use crate::Session;
fn editing() -> (Session, u64) {
let mut s = Session::with_new();
s.execute("slide.new", &json!({"layout": "blank"})).unwrap();
let id = s.execute("insert.textBox", &json!({"rect": [100, 50, 400, 40]})).unwrap()["id"].as_u64().unwrap();
s.execute("text.edit", &json!({"id": id})).unwrap();
s.execute("text.insert", &json!({"text": "a\tb"})).unwrap();
(s, id)
}
#[test]
fn ruler_reports_the_text_area_and_indents() {
let (mut s, _) = editing();
let r = s.execute("format.ruler", &json!({})).unwrap();
assert!((r["x"].as_f64().unwrap() - 107.2).abs() < 0.01, "{r}");
assert_eq!(r["rotated"], false);
assert_eq!(r["tabs"].as_array().unwrap().len(), 0);
s.execute("format.paragraph", &json!({"indentLeft": 36, "indentFirst": -18})).unwrap();
let r = s.execute("format.ruler", &json!({})).unwrap();
assert_eq!((r["marginLeft"].as_f64(), r["indent"].as_f64()), (Some(36.0), Some(-18.0)));
s.execute("text.exit", &json!({})).unwrap();
assert!(s.execute("format.ruler", &json!({})).is_err(), "only while editing text");
}
#[test]
fn tabs_add_move_remove_clear_and_undo() {
let (mut s, _) = editing();
let tabs = |s: &mut Session| -> Vec<(f64, String)> {
s.execute("format.ruler", &json!({})).unwrap()["tabs"]
.as_array()
.unwrap()
.iter()
.map(|t| (t["pos"].as_f64().unwrap(), t["align"].as_str().unwrap().to_string()))
.collect()
};
s.execute("format.tabs", &json!({"add": {"pos": 144, "align": "center"}})).unwrap();
s.execute("format.tabs", &json!({"add": {"pos": 72}})).unwrap();
assert_eq!(tabs(&mut s), vec![(72.0, "l".into()), (144.0, "ctr".into())], "sorted");
s.execute("format.tabs", &json!({"add": {"pos": 72.2, "align": "r"}})).unwrap();
assert_eq!(tabs(&mut s), vec![(72.2, "r".into()), (144.0, "ctr".into())], "a stop at the same place is replaced");
s.execute("format.tabs", &json!({"move": {"from": 144, "to": 200}})).unwrap();
s.execute("format.tabs", &json!({"remove": 72.2})).unwrap();
assert_eq!(tabs(&mut s), vec![(200.0, "ctr".into())]);
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(tabs(&mut s).len(), 2);
s.execute("format.tabs", &json!({"clear": true})).unwrap();
assert!(tabs(&mut s).is_empty());
s.ui_requests.clear();
s.execute("format.tabs", &json!({})).unwrap();
assert!(matches!(s.ui_requests.last(), Some(crate::UiRequest::Dialog { id, .. }) if id == "tabs"), "no fields opens the Tabs dialog");
assert!(s.execute("format.tabs", &json!({"add": {"pos": f64::MAX}})).is_ok(), "clamped, not a crash");
for i in 0..100 {
s.execute("format.tabs", &json!({"add": {"pos": i as f64 * 3.0}})).unwrap();
}
assert!(tabs(&mut s).len() <= 32);
s.execute("format.tabs", &json!({"set": [{"pos": 90, "align": "dec"}, {"pos": 30, "align": "right"}, {"pos": "x"}], "defaultTab": 36})).unwrap();
assert_eq!(tabs(&mut s), vec![(30.0, "r".into()), (90.0, "dec".into())], "set replaces all, sorted, bad entries dropped");
assert_eq!(s.execute("format.ruler", &json!({})).unwrap()["defaultTab"].as_f64(), Some(36.0));
}