Tests: the branch's dialog tests beside main's dialog test helpers (dialogs/tests.rs)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
moppeeerandClaude Opus 5.5 committed 2026-10-29 19:44:19 +01:00
1 parent 3b8e7c1284
commit c20579eb99
2 files changed
+200 -200

No files matched your search

-200
View File
@@ -186,206 +186,6 @@ fn color_theme(app: &DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
mod color_theme_tests {
use super::*;
/// The course's chapter 22: Find/Change › Change Format. "eggs", case sensitive, nothing in
/// Change to; Change Format Settings › Style Options sets Character Style "eggs", and the box
/// shows it (with Change to's ⓘ). Escape leaves the settings, not Find/Change. Change All
/// gives every lowercase "eggs" the style and keeps the words; the trash clears the format.
#[test]
fn find_change_gives_the_found_text_a_change_format() {
use egui_kittest::{Harness, kittest::Queryable};
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
let text = "Poached Eggs\nBreak eggs; cook until eggs are set.";
let f = app.run("frame.create", json!({"rect": [36, 36, 400, 300], "content": "text", "text": text})).unwrap();
let sid = designcraft_doc::StoryId(f["story"].as_u64().unwrap());
app.run("style.character.create", json!({"name": "eggs", "chars": {"fontStyle": "Bold"}})).unwrap();
app.run("app.findChange", json!({})).unwrap();
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("find".into(), json!("eggs"));
d.fields.insert("caseSensitive".into(), json!(true));
}
let mut h = Harness::builder().with_size(egui::vec2(1200.0, 900.0)).build_ui_state(
|ui, app: &mut DesignApp| {
let key = egui::Id::new("change_format_test_fonts");
if ui.data(|d| d.get_temp::<bool>(key)).is_none() {
crate::theme::install_fonts(ui.ctx(), "");
ui.data_mut(|d| d.insert_temp(key, true));
return;
}
show(app, ui.ctx());
},
app,
);
h.run_steps(4);
const TIP: &str = "With a Change Format and nothing in Change to, the found text stays and is only formatted";
assert!(h.query_by_label("Change Format:").is_some());
assert!(h.query_by_label(TIP).is_none(), "no ⓘ without a Change Format");
h.get_by_label("Specify attributes to change").click();
h.run_steps(3);
// Style Options is the section's name in the list and its heading.
for l in ["Change Format Settings", "Style Options", "Basic Character Formats", "Character Color"] {
assert!(h.query_all_by_label(l).next().is_some(), "{l}");
}
// Style Options › Character Style: eggs.
h.get_all_by_value("[Any Style]").next().unwrap().click();
h.run_steps(3);
h.get_by_label("eggs").click();
h.run_steps(3);
h.get_all_by_label(" OK ").last().unwrap().click();
h.run_steps(3);
let field = |h: &Harness<'_, DesignApp>, k: &str| h.state().ui.dialog.as_ref().unwrap().fields.get(k).cloned();
assert_eq!((field(&h, CHANGE_FORMAT), field(&h, FORMAT_EDIT)), (Some(json!({"charStyle": "eggs"})), None));
assert!(h.query_by_label("Character Style: eggs").is_some(), "the box shows it");
assert!(h.query_by_label(TIP).is_some(), "Change to's ⓘ");
// Escape closes Change Format Settings only, and keeps the Change Format.
h.get_by_label("Specify attributes to change").click();
h.run_steps(3);
h.key_press(egui::Key::Escape);
h.run_steps(3);
assert_eq!(h.state().ui.dialog.as_ref().map(|d| d.id.as_str()), Some("findChange"));
assert_eq!((field(&h, CHANGE_FORMAT), field(&h, FORMAT_EDIT)), (Some(json!({"charStyle": "eggs"})), None));
// Change All: the words stay, every lowercase "eggs" has the style.
h.get_by_label("Change All").click();
h.run_steps(3);
assert_eq!(field(&h, "status"), Some(json!("2 replacement(s) made")));
let story = h.state().session.doc().unwrap().doc.story(sid).cloned().unwrap();
assert_eq!(story.text, text);
assert_eq!(
[8, 19, 36].map(|at| story.format_after(at).style.clone()),
[designcraft_doc::NO_CHAR_STYLE.to_string(), "eggs".into(), "eggs".into()]
);
// The trash clears it (the second: the first is the Find Format's).
h.get_all_by_label("Clear specified attributes").nth(1).unwrap().click();
h.run_steps(3);
assert_eq!(field(&h, CHANGE_FORMAT), None);
assert!(h.query_by_label(TIP).is_none());
}
/// What Change Format Settings sets goes to find.change: a paragraph style and character
/// attributes as well as a character style; the box sums them up.
#[test]
fn a_change_format_goes_with_change_all() {
let mut d = Dialog::new("findChange", json!({"find": "cook"}));
d.fields.insert(CHANGE_FORMAT.into(), json!({"charStyle": "eggs", "paraStyle": "Method", "chars": {"underline": true}}));
let p = find_change_params(&d);
assert_eq!(
(p["charStyle"].clone(), p["paraStyle"].clone(), p["attrs"].clone()),
(json!("eggs"), json!("Method"), json!({"underline": true}))
);
assert_eq!(format_summary("en", d.fields.get(CHANGE_FORMAT)), "Character Style: eggs + Paragraph Style: Method + Underline: on");
assert!(find_change_params(&Dialog::new("findChange", json!({"find": "cook"}))).get("attrs").is_none());
// A Find Format goes with every search.
d.fields.insert(FIND_FORMAT.into(), json!({"paraStyle": "Method"}));
assert_eq!(find_change_params(&d)["findFormat"], json!({"paraStyle": "Method"}));
}
/// Find/Change › Find Format, as InDesign's: Find Format Settings sets the character style the
/// found text must have, the box shows it with Find what's ⓘ, and Count counts only "eggs" in
/// it; with nothing in Find what, Find Next finds the text in the style; the first trash clears
/// it.
#[test]
fn find_format_finds_only_text_with_it() {
use egui_kittest::{Harness, kittest::Queryable};
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
let f = app.run("frame.create", json!({"rect": [36, 36, 400, 300], "content": "text", "text": "Break eggs; cook eggs."})).unwrap();
app.run("style.character.create", json!({"name": "eggs", "chars": {"fontStyle": "Bold"}})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 6, "focus": 10})).unwrap();
app.run("style.character.apply", json!({"name": "eggs"})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 0, "focus": 0})).unwrap();
app.run("app.findChange", json!({})).unwrap();
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("find".into(), json!("eggs"));
}
let mut h = Harness::builder().with_size(egui::vec2(1200.0, 900.0)).build_ui_state(
|ui, app: &mut DesignApp| {
let key = egui::Id::new("find_format_test_fonts");
if ui.data(|d| d.get_temp::<bool>(key)).is_none() {
crate::theme::install_fonts(ui.ctx(), "");
ui.data_mut(|d| d.insert_temp(key, true));
return;
}
show(app, ui.ctx());
},
app,
);
h.run_steps(4);
const TIP: &str = "With a Find Format, only text that has it is found; with nothing in Find what, all text that has it";
assert!(h.query_by_label("Find Format:").is_some() && h.query_by_label(TIP).is_none());
h.get_by_label("Specify attributes to find").click();
h.run_steps(3);
assert!(h.query_by_label("Find Format Settings").is_some());
h.get_all_by_value("[Any Style]").next().unwrap().click();
h.run_steps(3);
h.get_by_label("eggs").click();
h.run_steps(3);
h.get_all_by_label(" OK ").last().unwrap().click();
h.run_steps(3);
let field = |h: &Harness<'_, DesignApp>, k: &str| h.state().ui.dialog.as_ref().unwrap().fields.get(k).cloned();
assert_eq!((field(&h, FIND_FORMAT), field(&h, CHANGE_FORMAT)), (Some(json!({"charStyle": "eggs"})), None));
assert!(h.query_by_label("Character Style: eggs").is_some() && h.query_by_label(TIP).is_some());
h.get_by_label("Count").click();
h.run_steps(3);
assert_eq!(field(&h, "status"), Some(json!("1 match(es)")), "only the \"eggs\" in the style");
// Nothing in Find what: the text in the style.
h.state_mut().ui.dialog.as_mut().unwrap().fields.insert("find".into(), json!(""));
h.run_steps(2);
h.get_by_label("Find Next").click();
h.run_steps(3);
let sel = h.state().session.doc().unwrap().selection.text.unwrap();
assert_eq!(sel.range(), 6..10);
// The first trash is the Find Format's.
h.get_all_by_label("Clear specified attributes").next().unwrap().click();
h.run_steps(3);
assert_eq!(field(&h, FIND_FORMAT), None);
}
/// Find/Change's Change and Change/Find, as InDesign's: Find Next selects a match, Change/Find
/// changes it and selects the next, Change changes that one and keeps it selected. With no
/// text selected they're off.
#[test]
fn change_and_change_find_change_the_found_text() {
use egui_kittest::Harness;
use egui_kittest::kittest::{NodeT, Queryable};
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
let f = app.run("frame.create", json!({"rect": [36, 36, 400, 300], "content": "text", "text": "red red | red"})).unwrap();
let sid = designcraft_doc::StoryId(f["story"].as_u64().unwrap());
app.run("text.select", json!({"story": sid.0, "anchor": 0, "focus": 0})).unwrap();
app.run("app.findChange", json!({})).unwrap();
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("find".into(), json!("red"));
d.fields.insert("change".into(), json!("blue"));
}
let mut h = Harness::builder().with_size(egui::vec2(1200.0, 900.0)).build_ui_state(
|ui, app: &mut DesignApp| {
let key = egui::Id::new("change_find_test_fonts");
if ui.data(|d| d.get_temp::<bool>(key)).is_none() {
crate::theme::install_fonts(ui.ctx(), "");
ui.data_mut(|d| d.insert_temp(key, true));
return;
}
show(app, ui.ctx());
},
app,
);
h.run_steps(4);
assert!(h.query_by_value("Document").is_some(), "the Search choices are InDesign's");
assert!(h.get_by_label("Change").accesskit_node().is_disabled(), "nothing found yet");
let text = |h: &Harness<'_, DesignApp>| h.state().session.doc().unwrap().doc.story(sid).unwrap().text.clone();
let selected = |h: &Harness<'_, DesignApp>| h.state().session.doc().unwrap().selection.text.unwrap().range();
h.get_by_label("Find Next").click();
h.run_steps(3);
assert_eq!(selected(&h), 0..3);
h.get_by_label("Change/Find").click();
h.run_steps(3);
assert_eq!((text(&h), selected(&h)), ("blue red | red".to_string(), 5..8));
h.get_by_label("Change").click();
h.run_steps(3);
assert_eq!((text(&h), selected(&h)), ("blue blue | red".to_string(), 5..9));
assert_eq!(h.state().ui.dialog.as_ref().unwrap().fields["status"], json!("1 replacement(s) made"));
}
#[test]
fn color_theme_cancel_is_read_only_and_confirm_adds_the_preview() {
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
+200
View File
@@ -3530,3 +3530,203 @@ fn character_style_options_set_the_digits() {
assert_eq!(ctl(&mut app, "ui.dialog.confirm", json!({}))["ok"], true);
assert_eq!(char_style(&app, "Emphasis").unwrap().chars.digits, Some(designcraft_doc::Digits::Farsi));
}
/// The course's chapter 22: Find/Change › Change Format. "eggs", case sensitive, nothing in
/// Change to; Change Format Settings › Style Options sets Character Style "eggs", and the box
/// shows it (with Change to's ⓘ). Escape leaves the settings, not Find/Change. Change All
/// gives every lowercase "eggs" the style and keeps the words; the trash clears the format.
#[test]
fn find_change_gives_the_found_text_a_change_format() {
use egui_kittest::{Harness, kittest::Queryable};
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
let text = "Poached Eggs\nBreak eggs; cook until eggs are set.";
let f = app.run("frame.create", json!({"rect": [36, 36, 400, 300], "content": "text", "text": text})).unwrap();
let sid = designcraft_doc::StoryId(f["story"].as_u64().unwrap());
app.run("style.character.create", json!({"name": "eggs", "chars": {"fontStyle": "Bold"}})).unwrap();
app.run("app.findChange", json!({})).unwrap();
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("find".into(), json!("eggs"));
d.fields.insert("caseSensitive".into(), json!(true));
}
let mut h = Harness::builder().with_size(egui::vec2(1200.0, 900.0)).build_ui_state(
|ui, app: &mut DesignApp| {
let key = egui::Id::new("change_format_test_fonts");
if ui.data(|d| d.get_temp::<bool>(key)).is_none() {
crate::theme::install_fonts(ui.ctx(), "");
ui.data_mut(|d| d.insert_temp(key, true));
return;
}
show(app, ui.ctx());
},
app,
);
h.run_steps(4);
const TIP: &str = "With a Change Format and nothing in Change to, the found text stays and is only formatted";
assert!(h.query_by_label("Change Format:").is_some());
assert!(h.query_by_label(TIP).is_none(), "no ⓘ without a Change Format");
h.get_by_label("Specify attributes to change").click();
h.run_steps(3);
// Style Options is the section's name in the list and its heading.
for l in ["Change Format Settings", "Style Options", "Basic Character Formats", "Character Color"] {
assert!(h.query_all_by_label(l).next().is_some(), "{l}");
}
// Style Options › Character Style: eggs.
h.get_all_by_value("[Any Style]").next().unwrap().click();
h.run_steps(3);
h.get_by_label("eggs").click();
h.run_steps(3);
h.get_all_by_label(" OK ").last().unwrap().click();
h.run_steps(3);
let field = |h: &Harness<'_, DesignApp>, k: &str| h.state().ui.dialog.as_ref().unwrap().fields.get(k).cloned();
assert_eq!((field(&h, CHANGE_FORMAT), field(&h, FORMAT_EDIT)), (Some(json!({"charStyle": "eggs"})), None));
assert!(h.query_by_label("Character Style: eggs").is_some(), "the box shows it");
assert!(h.query_by_label(TIP).is_some(), "Change to's ⓘ");
// Escape closes Change Format Settings only, and keeps the Change Format.
h.get_by_label("Specify attributes to change").click();
h.run_steps(3);
h.key_press(egui::Key::Escape);
h.run_steps(3);
assert_eq!(h.state().ui.dialog.as_ref().map(|d| d.id.as_str()), Some("findChange"));
assert_eq!((field(&h, CHANGE_FORMAT), field(&h, FORMAT_EDIT)), (Some(json!({"charStyle": "eggs"})), None));
// Change All: the words stay, every lowercase "eggs" has the style.
h.get_by_label("Change All").click();
h.run_steps(3);
assert_eq!(field(&h, "status"), Some(json!("2 replacement(s) made")));
let story = h.state().session.doc().unwrap().doc.story(sid).cloned().unwrap();
assert_eq!(story.text, text);
assert_eq!(
[8, 19, 36].map(|at| story.format_after(at).style.clone()),
[designcraft_doc::NO_CHAR_STYLE.to_string(), "eggs".into(), "eggs".into()]
);
// The trash clears it (the second: the first is the Find Format's).
h.get_all_by_label("Clear specified attributes").nth(1).unwrap().click();
h.run_steps(3);
assert_eq!(field(&h, CHANGE_FORMAT), None);
assert!(h.query_by_label(TIP).is_none());
}
/// What Change Format Settings sets goes to find.change: a paragraph style and character
/// attributes as well as a character style; the box sums them up.
#[test]
fn a_change_format_goes_with_change_all() {
let mut d = Dialog::new("findChange", json!({"find": "cook"}));
d.fields.insert(CHANGE_FORMAT.into(), json!({"charStyle": "eggs", "paraStyle": "Method", "chars": {"underline": true}}));
let p = find_change_params(&d);
assert_eq!(
(p["charStyle"].clone(), p["paraStyle"].clone(), p["attrs"].clone()),
(json!("eggs"), json!("Method"), json!({"underline": true}))
);
assert_eq!(format_summary("en", d.fields.get(CHANGE_FORMAT)), "Character Style: eggs + Paragraph Style: Method + Underline: on");
assert!(find_change_params(&Dialog::new("findChange", json!({"find": "cook"}))).get("attrs").is_none());
// A Find Format goes with every search.
d.fields.insert(FIND_FORMAT.into(), json!({"paraStyle": "Method"}));
assert_eq!(find_change_params(&d)["findFormat"], json!({"paraStyle": "Method"}));
}
/// Find/Change › Find Format, as InDesign's: Find Format Settings sets the character style the
/// found text must have, the box shows it with Find what's ⓘ, and Count counts only "eggs" in
/// it; with nothing in Find what, Find Next finds the text in the style; the first trash clears
/// it.
#[test]
fn find_format_finds_only_text_with_it() {
use egui_kittest::{Harness, kittest::Queryable};
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
let f = app.run("frame.create", json!({"rect": [36, 36, 400, 300], "content": "text", "text": "Break eggs; cook eggs."})).unwrap();
app.run("style.character.create", json!({"name": "eggs", "chars": {"fontStyle": "Bold"}})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 6, "focus": 10})).unwrap();
app.run("style.character.apply", json!({"name": "eggs"})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 0, "focus": 0})).unwrap();
app.run("app.findChange", json!({})).unwrap();
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("find".into(), json!("eggs"));
}
let mut h = Harness::builder().with_size(egui::vec2(1200.0, 900.0)).build_ui_state(
|ui, app: &mut DesignApp| {
let key = egui::Id::new("find_format_test_fonts");
if ui.data(|d| d.get_temp::<bool>(key)).is_none() {
crate::theme::install_fonts(ui.ctx(), "");
ui.data_mut(|d| d.insert_temp(key, true));
return;
}
show(app, ui.ctx());
},
app,
);
h.run_steps(4);
const TIP: &str = "With a Find Format, only text that has it is found; with nothing in Find what, all text that has it";
assert!(h.query_by_label("Find Format:").is_some() && h.query_by_label(TIP).is_none());
h.get_by_label("Specify attributes to find").click();
h.run_steps(3);
assert!(h.query_by_label("Find Format Settings").is_some());
h.get_all_by_value("[Any Style]").next().unwrap().click();
h.run_steps(3);
h.get_by_label("eggs").click();
h.run_steps(3);
h.get_all_by_label(" OK ").last().unwrap().click();
h.run_steps(3);
let field = |h: &Harness<'_, DesignApp>, k: &str| h.state().ui.dialog.as_ref().unwrap().fields.get(k).cloned();
assert_eq!((field(&h, FIND_FORMAT), field(&h, CHANGE_FORMAT)), (Some(json!({"charStyle": "eggs"})), None));
assert!(h.query_by_label("Character Style: eggs").is_some() && h.query_by_label(TIP).is_some());
h.get_by_label("Count").click();
h.run_steps(3);
assert_eq!(field(&h, "status"), Some(json!("1 match(es)")), "only the \"eggs\" in the style");
// Nothing in Find what: the text in the style.
h.state_mut().ui.dialog.as_mut().unwrap().fields.insert("find".into(), json!(""));
h.run_steps(2);
h.get_by_label("Find Next").click();
h.run_steps(3);
let sel = h.state().session.doc().unwrap().selection.text.unwrap();
assert_eq!(sel.range(), 6..10);
// The first trash is the Find Format's.
h.get_all_by_label("Clear specified attributes").next().unwrap().click();
h.run_steps(3);
assert_eq!(field(&h, FIND_FORMAT), None);
}
/// Find/Change's Change and Change/Find, as InDesign's: Find Next selects a match, Change/Find
/// changes it and selects the next, Change changes that one and keeps it selected. With no
/// text selected they're off.
#[test]
fn change_and_change_find_change_the_found_text() {
use egui_kittest::Harness;
use egui_kittest::kittest::{NodeT, Queryable};
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
let f = app.run("frame.create", json!({"rect": [36, 36, 400, 300], "content": "text", "text": "red red | red"})).unwrap();
let sid = designcraft_doc::StoryId(f["story"].as_u64().unwrap());
app.run("text.select", json!({"story": sid.0, "anchor": 0, "focus": 0})).unwrap();
app.run("app.findChange", json!({})).unwrap();
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("find".into(), json!("red"));
d.fields.insert("change".into(), json!("blue"));
}
let mut h = Harness::builder().with_size(egui::vec2(1200.0, 900.0)).build_ui_state(
|ui, app: &mut DesignApp| {
let key = egui::Id::new("change_find_test_fonts");
if ui.data(|d| d.get_temp::<bool>(key)).is_none() {
crate::theme::install_fonts(ui.ctx(), "");
ui.data_mut(|d| d.insert_temp(key, true));
return;
}
show(app, ui.ctx());
},
app,
);
h.run_steps(4);
assert!(h.query_by_value("Document").is_some(), "the Search choices are InDesign's");
assert!(h.get_by_label("Change").accesskit_node().is_disabled(), "nothing found yet");
let text = |h: &Harness<'_, DesignApp>| h.state().session.doc().unwrap().doc.story(sid).unwrap().text.clone();
let selected = |h: &Harness<'_, DesignApp>| h.state().session.doc().unwrap().selection.text.unwrap().range();
h.get_by_label("Find Next").click();
h.run_steps(3);
assert_eq!(selected(&h), 0..3);
h.get_by_label("Change/Find").click();
h.run_steps(3);
assert_eq!((text(&h), selected(&h)), ("blue red | red".to_string(), 5..8));
h.get_by_label("Change").click();
h.run_steps(3);
assert_eq!((text(&h), selected(&h)), ("blue blue | red".to_string(), 5..9));
assert_eq!(h.state().ui.dialog.as_ref().unwrap().fields["status"], json!("1 replacement(s) made"));
}