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:29 +01:00
1 parent d4a00a207f
commit 954e3158f1
2 files changed
+128 -128

No files matched your search

-128
View File
@@ -186,134 +186,6 @@ fn color_theme(app: &DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
mod color_theme_tests {
use super::*;
/// The course's chapter 20: the "Recipe Categories" title formatted on the page (size, All
/// Caps, tracking, space after), then New Paragraph Style from it: the dialog starts from the
/// text, and with Apply Style to Selection the paragraph takes the new style. Next Style and
/// Reset To Base are set from General.
#[test]
fn a_new_paragraph_style_starts_from_the_selected_text() {
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": "Egg Recipes\rBoil them."})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 0, "focus": 11})).unwrap();
app.run("type.char", json!({"attrs": {"size": 20, "capitalization": "allCaps", "tracking": 50}})).unwrap();
app.run("type.para", json!({"attrs": {"spaceAfter": 6}})).unwrap();
open_new_style(&mut app, true);
let d = app.ui.dialog.clone().unwrap();
assert_eq!(
(d.fields["c.size"].clone(), d.fields["c.capitalization"].clone(), d.fields["p.spaceAfter"].clone()),
(json!(20.0), json!("allCaps"), json!(6.0))
);
assert!(d.b("apply") && !d.fields.contains_key("c.fontFamily"), "only what differs from [No Paragraph Style]");
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("rename".into(), json!("Recipe Categories"));
d.fields.insert("nextStyle".into(), json!(designcraft_doc::BASIC_PARAGRAPH));
}
confirm(&mut app).unwrap();
let a = app.run("type.selectionAttrs", json!({})).unwrap();
assert_eq!(a["paragraphStyle"], "Recipe Categories", "applied to the selection");
assert_eq!(a["paraOverrides"].as_u64().unwrap_or(0) + a["charOverrides"].as_u64().unwrap_or(0), 0);
let style = |app: &DesignApp| app.session.doc().unwrap().doc.styles.para("Recipe Categories").cloned().unwrap();
assert_eq!(style(&app).next_style.as_deref(), Some(designcraft_doc::BASIC_PARAGRAPH));
assert_eq!(style(&app).chars.size, Some(20.0));
// Reset To Base: the style keeps nothing of its own, apart from the edits made then.
app.ui.dialog = Some(Dialog::new("paragraphStyleOptions", json!({"name": "Recipe Categories", "basedOn": designcraft_doc::BASIC_PARAGRAPH})));
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("resetToBase".into(), json!(true));
d.fields.insert("c.tracking".into(), json!(10.0));
}
confirm(&mut app).unwrap();
let s = style(&app);
assert_eq!((s.chars.size, s.chars.tracking, s.para.space_after), (None, Some(10.0), None), "{s:?}");
assert_eq!(s.based_on.as_deref(), Some(designcraft_doc::BASIC_PARAGRAPH));
}
/// The course's chapter 22: "eggs" made bold and red on the page, then New Character Style
/// from it. The dialog is titled as InDesign's, starts from what the text has of its own
/// over its paragraph style ("[None] + Bold + color"), and Apply Style to Selection gives the
/// text the style, with no override left.
#[test]
fn a_new_character_style_starts_from_the_selected_text() {
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": "Drop eggs into boiling water."})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 0, "focus": 29})).unwrap();
app.run("style.paragraph.create", json!({"name": "Recipe", "chars": {"size": 14}})).unwrap();
app.run("style.paragraph.apply", json!({"name": "Recipe"})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 5, "focus": 9})).unwrap();
app.run("type.char", json!({"attrs": {"fontStyle": "Bold", "fill": "C=15 M=100 Y=100 K=0"}})).unwrap();
open_new_style(&mut app, false);
let d = app.ui.dialog.clone().unwrap();
assert_eq!((d.fields["c.fontStyle"].clone(), d.fields["c.fill"].clone()), (json!("Bold"), json!("C=15 M=100 Y=100 K=0")));
assert!(d.b("apply"));
let mut h = Harness::builder().with_size(egui::vec2(1000.0, 700.0)).build_ui_state(
|ui, app: &mut DesignApp| {
let key = egui::Id::new("new_character_style_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);
for l in ["New Character Style", "Apply Style to Selection"] {
assert!(h.query_by_label(l).is_some(), "{l}");
}
let app = h.state_mut();
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("rename".into(), json!("eggs"));
}
confirm(app).unwrap();
let style = app.session.doc().unwrap().doc.styles.char_style("eggs").cloned().unwrap();
assert_eq!((style.chars.font_style.as_deref(), style.chars.fill.as_deref()), (Some("Bold"), Some("C=15 M=100 Y=100 K=0")));
assert_eq!(style.chars.size, None, "the size is the paragraph style's, not the text's own");
let a = app.run("type.selectionAttrs", json!({})).unwrap();
assert_eq!((a["characterStyle"].clone(), a["charOverrides"].clone()), (json!("eggs"), json!(0)), "applied to the selection");
}
/// A Based On changed in the dialog (without Reset To Base) is what the fields are checked
/// against: the style's own settings over the new parent.
#[test]
fn a_changed_based_on_is_what_the_fields_are_checked_against() {
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
app.run("style.paragraph.create", json!({"name": "Loose", "para": {"wordSpaceMin": 1.2, "wordSpaceDesired": 1.3}})).unwrap();
app.run("style.paragraph.create", json!({"name": "Child", "basedOn": "Loose"})).unwrap();
let mut d = Dialog::new("paragraphStyleOptions", json!({"name": "Child"}));
d.fields.insert("p.wordSpaceDesired".into(), json!(1.0));
assert!(check_paragraph_style_justification(&app, &d).is_err(), "below Loose's minimum");
d.fields.insert("basedOn".into(), json!(designcraft_doc::BASIC_PARAGRAPH));
assert_eq!(check_paragraph_style_justification(&app, &d), Ok(()), "[Basic Paragraph]'s minimum is lower");
}
/// With Reset To Base, the justification fields are checked against the base style (the
/// style's own minimum no longer applies), and Preview shows the reset on the page.
#[test]
fn reset_to_base_checks_and_previews_against_the_base() {
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": "Loose words"})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 0, "focus": 5})).unwrap();
app.run("style.paragraph.create", json!({"name": "Loose", "basedOn": designcraft_doc::BASIC_PARAGRAPH, "para": {"wordSpaceMin": 1.2, "wordSpaceDesired": 1.3}, "chars": {"size": 30}}))
.unwrap();
app.run("style.paragraph.apply", json!({"name": "Loose"})).unwrap();
let mut d = Dialog::new("paragraphStyleOptions", json!({"name": "Loose"}));
d.fields.insert("p.wordSpaceDesired".into(), json!(1.1));
assert!(check_paragraph_style_justification(&app, &d).is_err(), "below the style's own minimum");
d.fields.insert("resetToBase".into(), json!(true));
assert_eq!(check_paragraph_style_justification(&app, &d), Ok(()), "the base's minimum is 100%");
d.fields.insert("preview".into(), json!(true));
style_options_preview(&mut app, &mut d);
assert_eq!(d.fields[PREVIEWED]["resetToBase"], true);
let size = app.run("type.selectionAttrs", json!({})).unwrap()["chars"]["size"].clone();
assert_eq!(size, json!(12.0), "the page shows the style reset to its base");
app.session.cancel_interaction();
}
#[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());
+128
View File
@@ -3524,3 +3524,131 @@ 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 20: the "Recipe Categories" title formatted on the page (size, All
/// Caps, tracking, space after), then New Paragraph Style from it: the dialog starts from the
/// text, and with Apply Style to Selection the paragraph takes the new style. Next Style and
/// Reset To Base are set from General.
#[test]
fn a_new_paragraph_style_starts_from_the_selected_text() {
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": "Egg Recipes\rBoil them."})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 0, "focus": 11})).unwrap();
app.run("type.char", json!({"attrs": {"size": 20, "capitalization": "allCaps", "tracking": 50}})).unwrap();
app.run("type.para", json!({"attrs": {"spaceAfter": 6}})).unwrap();
open_new_style(&mut app, true);
let d = app.ui.dialog.clone().unwrap();
assert_eq!(
(d.fields["c.size"].clone(), d.fields["c.capitalization"].clone(), d.fields["p.spaceAfter"].clone()),
(json!(20.0), json!("allCaps"), json!(6.0))
);
assert!(d.b("apply") && !d.fields.contains_key("c.fontFamily"), "only what differs from [No Paragraph Style]");
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("rename".into(), json!("Recipe Categories"));
d.fields.insert("nextStyle".into(), json!(designcraft_doc::BASIC_PARAGRAPH));
}
confirm(&mut app).unwrap();
let a = app.run("type.selectionAttrs", json!({})).unwrap();
assert_eq!(a["paragraphStyle"], "Recipe Categories", "applied to the selection");
assert_eq!(a["paraOverrides"].as_u64().unwrap_or(0) + a["charOverrides"].as_u64().unwrap_or(0), 0);
let style = |app: &DesignApp| app.session.doc().unwrap().doc.styles.para("Recipe Categories").cloned().unwrap();
assert_eq!(style(&app).next_style.as_deref(), Some(designcraft_doc::BASIC_PARAGRAPH));
assert_eq!(style(&app).chars.size, Some(20.0));
// Reset To Base: the style keeps nothing of its own, apart from the edits made then.
app.ui.dialog = Some(Dialog::new("paragraphStyleOptions", json!({"name": "Recipe Categories", "basedOn": designcraft_doc::BASIC_PARAGRAPH})));
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("resetToBase".into(), json!(true));
d.fields.insert("c.tracking".into(), json!(10.0));
}
confirm(&mut app).unwrap();
let s = style(&app);
assert_eq!((s.chars.size, s.chars.tracking, s.para.space_after), (None, Some(10.0), None), "{s:?}");
assert_eq!(s.based_on.as_deref(), Some(designcraft_doc::BASIC_PARAGRAPH));
}
/// The course's chapter 22: "eggs" made bold and red on the page, then New Character Style
/// from it. The dialog is titled as InDesign's, starts from what the text has of its own
/// over its paragraph style ("[None] + Bold + color"), and Apply Style to Selection gives the
/// text the style, with no override left.
#[test]
fn a_new_character_style_starts_from_the_selected_text() {
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": "Drop eggs into boiling water."})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 0, "focus": 29})).unwrap();
app.run("style.paragraph.create", json!({"name": "Recipe", "chars": {"size": 14}})).unwrap();
app.run("style.paragraph.apply", json!({"name": "Recipe"})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 5, "focus": 9})).unwrap();
app.run("type.char", json!({"attrs": {"fontStyle": "Bold", "fill": "C=15 M=100 Y=100 K=0"}})).unwrap();
open_new_style(&mut app, false);
let d = app.ui.dialog.clone().unwrap();
assert_eq!((d.fields["c.fontStyle"].clone(), d.fields["c.fill"].clone()), (json!("Bold"), json!("C=15 M=100 Y=100 K=0")));
assert!(d.b("apply"));
let mut h = Harness::builder().with_size(egui::vec2(1000.0, 700.0)).build_ui_state(
|ui, app: &mut DesignApp| {
let key = egui::Id::new("new_character_style_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);
for l in ["New Character Style", "Apply Style to Selection"] {
assert!(h.query_by_label(l).is_some(), "{l}");
}
let app = h.state_mut();
if let Some(d) = app.ui.dialog.as_mut() {
d.fields.insert("rename".into(), json!("eggs"));
}
confirm(app).unwrap();
let style = app.session.doc().unwrap().doc.styles.char_style("eggs").cloned().unwrap();
assert_eq!((style.chars.font_style.as_deref(), style.chars.fill.as_deref()), (Some("Bold"), Some("C=15 M=100 Y=100 K=0")));
assert_eq!(style.chars.size, None, "the size is the paragraph style's, not the text's own");
let a = app.run("type.selectionAttrs", json!({})).unwrap();
assert_eq!((a["characterStyle"].clone(), a["charOverrides"].clone()), (json!("eggs"), json!(0)), "applied to the selection");
}
/// A Based On changed in the dialog (without Reset To Base) is what the fields are checked
/// against: the style's own settings over the new parent.
#[test]
fn a_changed_based_on_is_what_the_fields_are_checked_against() {
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
app.run("style.paragraph.create", json!({"name": "Loose", "para": {"wordSpaceMin": 1.2, "wordSpaceDesired": 1.3}})).unwrap();
app.run("style.paragraph.create", json!({"name": "Child", "basedOn": "Loose"})).unwrap();
let mut d = Dialog::new("paragraphStyleOptions", json!({"name": "Child"}));
d.fields.insert("p.wordSpaceDesired".into(), json!(1.0));
assert!(check_paragraph_style_justification(&app, &d).is_err(), "below Loose's minimum");
d.fields.insert("basedOn".into(), json!(designcraft_doc::BASIC_PARAGRAPH));
assert_eq!(check_paragraph_style_justification(&app, &d), Ok(()), "[Basic Paragraph]'s minimum is lower");
}
/// With Reset To Base, the justification fields are checked against the base style (the
/// style's own minimum no longer applies), and Preview shows the reset on the page.
#[test]
fn reset_to_base_checks_and_previews_against_the_base() {
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": "Loose words"})).unwrap();
app.run("text.select", json!({"story": f["story"], "anchor": 0, "focus": 5})).unwrap();
app.run("style.paragraph.create", json!({"name": "Loose", "basedOn": designcraft_doc::BASIC_PARAGRAPH, "para": {"wordSpaceMin": 1.2, "wordSpaceDesired": 1.3}, "chars": {"size": 30}}))
.unwrap();
app.run("style.paragraph.apply", json!({"name": "Loose"})).unwrap();
let mut d = Dialog::new("paragraphStyleOptions", json!({"name": "Loose"}));
d.fields.insert("p.wordSpaceDesired".into(), json!(1.1));
assert!(check_paragraph_style_justification(&app, &d).is_err(), "below the style's own minimum");
d.fields.insert("resetToBase".into(), json!(true));
assert_eq!(check_paragraph_style_justification(&app, &d), Ok(()), "the base's minimum is 100%");
d.fields.insert("preview".into(), json!(true));
style_options_preview(&mut app, &mut d);
assert_eq!(d.fields[PREVIEWED]["resetToBase"], true);
let size = app.run("type.selectionAttrs", json!({})).unwrap()["chars"]["size"].clone();
assert_eq!(size, json!(12.0), "the page shows the style reset to its base");
app.session.cancel_interaction();
}