Files
deckcraft/crates/ui-egui/src/replacefonts_tests.rs
T
Nikita RakitinandCursor 37bd53c62e Home: Replace Fonts dialog and font.replace command
font.replace {from, to} renames a font family everywhere it is named
explicitly: runs (latin, east Asian, complex script and symbol faces), end
marks, bullet fonts, list styles of shapes, layouts and masters, the master
text styles, the deck default style, notes, tables and the theme fonts, so
placeholders that follow the theme change too. Names compare
case-insensitively, theme references (+mn-lt) are left alone, a replace that
finds nothing adds no undo step, and missing fonts are recomputed.
font.used lists the families the slides draw in, for the dialog's Replace
list.

Home gets a Replace Fonts… button opening the dialog: Replace (fonts in the
deck), With (type or pick from installed fonts), and it stays open with a
confirmation like PowerPoint's. Ribbon small buttons now expose their label
to accessibility, which the UI test uses to click it.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 22:20:09 +03:00

71 lines
2.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Home › Replace Fonts…: the dialog lists the deck's fonts and replaces one, as one undo step.
use deckcraft_engine::Session;
use egui_kittest::Harness;
use egui_kittest::kittest::Queryable;
use serde_json::json;
use crate::{Services, SlideApp};
fn harness() -> Harness<'static, SlideApp> {
let mut s = Session::with_new();
s.execute("slide.new", &json!({"layout": "blank"})).unwrap();
s.execute("insert.textBox", &json!({"rect": [40, 40, 400, 80], "text": "Hello fonts"})).unwrap();
s.execute("text.exit", &json!({})).unwrap();
s.execute("format.font", &json!({"family": "Comic Neue"})).unwrap();
s.execute("format.size", &json!({"size": 40})).unwrap();
let app = SlideApp::new(s, Services::default());
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).with_step_dt(1.0 / 60.0).build_ui_state(
|ui, app: &mut SlideApp| {
app.logic(ui.ctx());
app.ui(ui);
},
app,
);
h.run_steps(3);
h
}
fn run_font(h: &Harness<'_, SlideApp>) -> Option<String> {
let st = h.state().session.active().unwrap();
st.current_slide()
.unwrap()
.shapes
.iter()
.find_map(|s| s.text.as_ref())
.and_then(|b| b.paragraphs.first())
.and_then(|p| p.runs.first())
.and_then(|r| r.props.font.clone())
}
#[test]
fn ribbon_button_opens_the_dialog_and_replaces() {
let mut h = harness();
h.get_by_label("Replace Fonts…").click();
h.run_steps(3);
assert_eq!(h.state().dialog.as_ref().map(|d| d.id.as_str()), Some("replaceFonts"));
// Pick the font to replace and type the new one, the way a person would.
if let Some(d) = h.state_mut().dialog.as_mut() {
d.fields.insert("from".into(), "Comic Neue".into());
d.fields.insert("to".into(), "Lato".into());
}
h.run_steps(2);
h.get_by_label("Replace").click();
h.run_steps(3);
assert_eq!(run_font(&h).as_deref(), Some("Lato"));
// The dialog stays open with a confirmation, like PowerPoint's.
assert!(h.query_by_label_contains("Replaced “Comic Neue”").is_some());
h.state_mut().session.execute("edit.undo", &json!({})).unwrap();
assert_eq!(run_font(&h).as_deref(), Some("Comic Neue"));
}
#[test]
#[ignore = "writes a screenshot for review"]
fn screenshot() {
let mut h = harness();
h.render().unwrap().save("/tmp/replacefonts0.png").unwrap();
h.state_mut().dialog = Some(crate::dialogs::Dialog::new("replaceFonts"));
h.run_steps(4);
h.render().unwrap().save("/tmp/replacefonts.png").unwrap();
}