diff --git a/crates/ui-egui/src/i18n/mod.rs b/crates/ui-egui/src/i18n/mod.rs index c6bd7ced..8eb90b02 100644 --- a/crates/ui-egui/src/i18n/mod.rs +++ b/crates/ui-egui/src/i18n/mod.rs @@ -218,7 +218,9 @@ pub fn lang_from_tag(tag: &str) -> Option { /// The system language (cached). English when it can't be determined. pub fn system_lang() -> Lang { - // Tests drive the UI by its English labels whatever the developer's locale is. + // Tests drive the UI by its English labels whatever the developer's locale is. This covers + // the unit tests; the integration tests link the library as it ships, where `cfg!(test)` is + // off, so each of their harnesses pins the `language` option to `en` instead. if cfg!(test) { return Lang::EN; } @@ -450,10 +452,14 @@ mod tests { #[cfg(target_os = "windows")] #[test] fn windows_display_language_query_returns_a_locale_tag() { - let tag = windows_ui_language().expect("Windows should report a display language"); - let tag = tag.trim(); - assert!(!tag.is_empty()); - assert!(tag.split('-').all(|part| !part.is_empty() && part.bytes().all(|b| b.is_ascii_alphanumeric()))); + // One tag per line, as `first_supported` reads them: a machine with several display + // languages reports them all, so each line is checked rather than the list as a whole. + let list = windows_ui_language().expect("Windows should report a display language"); + let tags: Vec<&str> = list.lines().map(str::trim).filter(|t| !t.is_empty()).collect(); + assert!(!tags.is_empty(), "no display language in {list:?}"); + for tag in tags { + assert!(tag.split('-').all(|part| !part.is_empty() && part.bytes().all(|b| b.is_ascii_alphanumeric())), "not a locale tag: {tag:?}"); + } } #[test] diff --git a/crates/ui-egui/tests/accessibility.rs b/crates/ui-egui/tests/accessibility.rs index 93074a97..b4be8841 100644 --- a/crates/ui-egui/tests/accessibility.rs +++ b/crates/ui-egui/tests/accessibility.rs @@ -38,6 +38,7 @@ fn checking_fixing_skipping_and_reporting() { let d = dir.clone(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("notes.pdf", None, FIXTURE.to_vec()).unwrap(); app.export_dir_override = Some(d.to_string_lossy().into_owned()); app @@ -110,6 +111,7 @@ trailer << /Root 1 0 R >> fn setting_alternate_text_figure_by_figure() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("figures.pdf", None, FIGURES.to_vec()).unwrap(); app }); diff --git a/crates/ui-egui/tests/actions.rs b/crates/ui-egui/tests/actions.rs index a1ed7ed2..dd4da8d3 100644 --- a/crates/ui-egui/tests/actions.rs +++ b/crates/ui-egui/tests/actions.rs @@ -17,6 +17,7 @@ fn run_and_create_actions() { let d = dir.clone(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.run_inline = true; app.export_dir_override = Some(d.join("out").to_string_lossy().into_owned()); app.action_files_override = Some(vec![d.join("r.pdf").to_string_lossy().into_owned()]); @@ -49,6 +50,7 @@ fn run_and_create_actions() { h.get_by_label("1. Add watermark: DRAFT"); let saved = h.state().persist(); let mut fresh = PdfCraftApp::new(); + fresh.set_option("language", "en").unwrap(); fresh.restore(&saved); assert_eq!(fresh.custom_actions, h.state().custom_actions); h.get_by_label("Start").click(); diff --git a/crates/ui-egui/tests/bookmarks.rs b/crates/ui-egui/tests/bookmarks.rs index ea93ad35..8288b046 100644 --- a/crates/ui-egui/tests/bookmarks.rs +++ b/crates/ui-egui/tests/bookmarks.rs @@ -17,6 +17,7 @@ trailer << /Root 1 0 R >> fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("pages.pdf", None, PAGES.to_vec()).expect("opens"); app.set_option("left", "closed").unwrap(); app.set_option("panel", "bookmarks").unwrap(); diff --git a/crates/ui-egui/tests/buttons.rs b/crates/ui-egui/tests/buttons.rs index 9c9481d6..3fae947d 100644 --- a/crates/ui-egui/tests/buttons.rs +++ b/crates/ui-egui/tests/buttons.rs @@ -57,6 +57,7 @@ fn buttons_reset_navigate_and_print() { let path_str = path.to_string_lossy().into_owned(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("buttons.pdf", Some(path_str), fixture()).unwrap(); app.set_option("zoom", "150").unwrap(); app @@ -124,6 +125,7 @@ fn name_hidden(h: &Harness<'static, PdfCraftApp>) -> bool { fn hide_actions_hide_and_show_fields() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("show-hide.pdf", None, show_hide_fixture()).unwrap(); app.set_option("zoom", "150").unwrap(); app diff --git a/crates/ui-egui/tests/commands.rs b/crates/ui-egui/tests/commands.rs index 55058b4f..27ac8304 100644 --- a/crates/ui-egui/tests/commands.rs +++ b/crates/ui-egui/tests/commands.rs @@ -35,6 +35,7 @@ fn fixture(n: usize) -> Vec { fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, fixture(3)).unwrap(); app }); @@ -70,6 +71,7 @@ fn every_registered_command_is_implemented() { continue; } let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); if spec.id.starts_with("form.") || spec.id == "comment.flatten" { app.open_bytes("form.pdf", None, include_bytes!("data/form.pdf").to_vec()).unwrap(); } else { @@ -139,6 +141,7 @@ fn every_registered_command_is_implemented() { #[test] fn disabled_commands_explain_themselves() { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); assert!(!app.execute("file.save")); assert_eq!(app.toast.as_ref().map(|t| t.0.as_str()), Some("Open a document first")); app.open_bytes("doc.pdf", None, fixture(2)).unwrap(); @@ -206,6 +209,7 @@ fn the_open_recent_menu_lists_files_and_opens_one() { let path = path.clone(); move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, fixture(3)).unwrap(); app.recent.push(pdfcraft_ui_egui::RecentFile { name: "recent.pdf".into(), path, pages: 2, size: 0 }); app diff --git a/crates/ui-egui/tests/comments.rs b/crates/ui-egui/tests/comments.rs index 51ae14d7..688a962e 100644 --- a/crates/ui-egui/tests/comments.rs +++ b/crates/ui-egui/tests/comments.rs @@ -26,6 +26,7 @@ trailer << /Root 1 0 R >> fn harness(setup: impl FnOnce(&mut PdfCraftApp) + 'static) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("text.pdf", None, TEXT_FIXTURE.to_vec()).expect("opens"); app.set_option("left", "closed").unwrap(); app.set_option("author", "Tester").unwrap(); @@ -539,6 +540,7 @@ fn hovering_a_comment_shows_its_author_and_text() { ); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("hover.pdf", None, pdf.into_bytes()).expect("opens"); app.set_option("left", "closed").unwrap(); // No Comments panel: the comment's text should only appear in the hover popup. diff --git a/crates/ui-egui/tests/compare.rs b/crates/ui-egui/tests/compare.rs index 2264f57f..a06d2b07 100644 --- a/crates/ui-egui/tests/compare.rs +++ b/crates/ui-egui/tests/compare.rs @@ -13,6 +13,7 @@ fn compare_two_versions() { let v2 = s.create_from_text("t", "Delivery within three days. Returns accepted for a week.").unwrap().to_vec(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("v1.pdf", None, v1.clone()).unwrap(); app.open_bytes("v2.pdf", None, v2.clone()).unwrap(); app @@ -49,6 +50,7 @@ fn compare_panel_screenshot() { let v2 = s.create_from_text("t", "Delivery within three days. Returns accepted for a week.").unwrap().to_vec(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("v1.pdf", None, v1.clone()).unwrap(); app.open_bytes("v2.pdf", None, v2.clone()).unwrap(); app @@ -68,6 +70,7 @@ fn pdfa_dialog_verifies_and_converts() { let doc = Session::new().create_from_text("t", "Keep forever").unwrap().to_vec(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("keep.pdf", None, doc.clone()).unwrap(); app }); @@ -93,6 +96,7 @@ fn export_to_word_html_and_rtf() { let dir = std::env::temp_dir().join(format!("pdfcraft-office-ui-{}", std::process::id())); std::fs::create_dir_all(&dir).unwrap(); let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("e.pdf", None, doc).unwrap(); for ext in ["docx", "html", "rtf"] { let out = dir.join(format!("e.{ext}")); diff --git a/crates/ui-egui/tests/content.rs b/crates/ui-egui/tests/content.rs index aec39001..eefec1ce 100644 --- a/crates/ui-egui/tests/content.rs +++ b/crates/ui-egui/tests/content.rs @@ -9,6 +9,7 @@ use pdfcraft_ui_egui::{PdfCraftApp, QuickTool}; fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, include_bytes!("data/form.pdf").to_vec()).unwrap(); app.set_option("zoom", "150").unwrap(); app diff --git a/crates/ui-egui/tests/control.rs b/crates/ui-egui/tests/control.rs index 634fac36..4326e1e8 100644 --- a/crates/ui-egui/tests/control.rs +++ b/crates/ui-egui/tests/control.rs @@ -43,6 +43,7 @@ fn harness_pages(pages: usize) -> (Harness<'static, PdfCraftApp>, ControlClient) let s = slot.clone(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); *s.lock().unwrap() = Some(app.attach_control(&cc.egui_ctx)); app.open_bytes("doc.pdf", None, fixture(pages)).unwrap(); app diff --git a/crates/ui-egui/tests/create.rs b/crates/ui-egui/tests/create.rs index 9f5390bd..f8f0e339 100644 --- a/crates/ui-egui/tests/create.rs +++ b/crates/ui-egui/tests/create.rs @@ -54,6 +54,7 @@ fn image_import_dialog_chooses_dpi_and_cancels() { #[test] fn opening_images_and_text_converts_them_to_new_pdfs() { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("photo.png", Some("/tmp/photo.png".into()), png()).unwrap(); app.open_bytes("notes.txt", None, b"first line\nsecond line".to_vec()).unwrap(); app.create_from_images(vec![("a.png".into(), png()), ("b.png".into(), png())]); @@ -77,6 +78,7 @@ fn reduce_file_size_writes_a_compact_copy() { let out2 = out.clone(); let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("notes.txt", None, "lorem ipsum ".repeat(500).into_bytes()).unwrap(); app.save_override = Some(out2.to_string_lossy().into_owned()); app @@ -100,6 +102,7 @@ fn reduce_file_size_writes_a_compact_copy() { fn clipboard_images_and_text_become_new_pdfs() { use pdfcraft_ui_egui::Clip; let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.create_from_clip(Clip::Image { width: 40, height: 20, rgba: [10u8, 20, 30, 255].repeat(40 * 20) }).unwrap(); app.create_from_clip(Clip::Text("Pasted\nlines".into())).unwrap(); let docs = app.session.docs(); @@ -121,6 +124,7 @@ fn the_pdf_optimizer_dialog_saves_an_optimized_copy() { let out2 = out.clone(); let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("notes.txt", None, "lorem ipsum ".repeat(500).into_bytes()).unwrap(); app.save_override = Some(out2.to_string_lossy().into_owned()); app @@ -211,6 +215,7 @@ fn multiple_files_open_as_one_document_in_the_page_grid() { #[test] fn multiple_files_that_cannot_be_converted_open_nothing() { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.use_files(pdfcraft_ui_egui::FilePurpose::CreateMultiple, vec![("report.docx".into(), b"PK\x03\x04".to_vec())]); assert!(app.views.is_empty()); assert!(app.toast.is_some()); diff --git a/crates/ui-egui/tests/document_links.rs b/crates/ui-egui/tests/document_links.rs index a866a916..1951d24f 100644 --- a/crates/ui-egui/tests/document_links.rs +++ b/crates/ui-egui/tests/document_links.rs @@ -44,6 +44,7 @@ fn fixture() -> Vec { fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("links.pdf", None, fixture()).unwrap(); app.set_option("left", "closed").unwrap(); app.set_option("panel", "none").unwrap(); diff --git a/crates/ui-egui/tests/editing.rs b/crates/ui-egui/tests/editing.rs index 10734782..99c0ec11 100644 --- a/crates/ui-egui/tests/editing.rs +++ b/crates/ui-egui/tests/editing.rs @@ -37,6 +37,7 @@ fn fixture(n: usize) -> Vec { fn harness(pages: usize, setup: impl FnOnce(&mut PdfCraftApp) + 'static) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, fixture(pages)).expect("fixture opens"); setup(&mut app); app @@ -395,6 +396,7 @@ fn save_prompt_stays_inside_the_screen_for_a_long_filename() { Vliek, Ed Sutherland, -- 5, 2024 -- McGraw-Hill Education (UK) Ltd -- isbn13 97815268.pdf"; let mut h = Harness::builder().with_size(egui::vec2(1365.0, 719.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes(name, None, fixture(1)).expect("fixture opens"); app.close_request = Some(CloseRequest::Tab(app.views[0].id)); app @@ -429,6 +431,7 @@ fn save_prompt_fits_the_smallest_window_whatever_the_name() { let start: String = name.chars().take(10).collect(); let mut h = Harness::builder().with_size(size).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes(&name, None, fixture(1)).expect("fixture opens"); app.close_request = Some(CloseRequest::Tab(app.views[0].id)); app @@ -818,6 +821,7 @@ fn columns_resize_and_the_layout_is_kept_in_the_settings() { assert!(widths[size_index] > 120.0, "{widths:?}"); let saved = h.state().persist(); let mut fresh = PdfCraftApp::new(); + fresh.set_option("language", "en").unwrap(); fresh.restore(&saved); assert_eq!(fresh.combine_columns, h.state().combine_columns); // Malformed settings give the default layout. @@ -1123,6 +1127,7 @@ fn save_pages_writes_what_the_grid_shows() { h.run_steps(4); assert!(!dirty(&h)); let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("saved.pdf", None, std::fs::read(&path).unwrap()).unwrap(); assert_eq!(texts_of(&app, 0), ["Page 1", "Page 3"]); } @@ -1194,6 +1199,7 @@ fn protected_with(algorithm: pdfcraft_cos::Algorithm, user: &str, owner: &str, p fn password_prompt_opens_and_security_tab_reports_the_details() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("secret.pdf", None, protected("pw", "owner", -1)).unwrap(); app }); @@ -1218,6 +1224,7 @@ fn password_prompt_opens_and_security_tab_reports_the_details() { fn restricted_documents_show_a_notice_and_block_page_changes() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("locked.pdf", None, protected("", "owner", 0b0100)).unwrap(); // opens without a password app.set_option("organize", "on").unwrap(); app @@ -1240,6 +1247,7 @@ fn restricted_documents_show_a_notice_and_block_page_changes() { #[test] fn replace_pages_dialog_swaps_page_content() { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, fixture(3)).unwrap(); app.views[0].select_pages(&[1]); app.start_replace("other.pdf".into(), fixture(5)); @@ -1365,6 +1373,7 @@ fn source_font_fixture() -> Vec { fn open_source_font_fixture() -> Harness<'static, PdfCraftApp> { Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("fonts.pdf", None, source_font_fixture()).expect("font fixture opens"); app }) @@ -1465,6 +1474,7 @@ fn double_drawn() -> Vec { fn editing_a_double_drawn_line_replaces_every_copy() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("bold.pdf", None, double_drawn()).expect("opens"); app }); @@ -1497,6 +1507,7 @@ fn editing_existing_images_on_the_page() { image::RgbImage::from_pixel(80, 40, image::Rgb([200, 40, 40])).write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).unwrap(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("picture.png", None, png.clone()).expect("opens"); app }); @@ -1622,6 +1633,7 @@ fn dragging_a_paragraph_moves_it_and_its_edge_rewraps_it() { fn pages_panel(pages: usize) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 1900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, fixture(pages)).expect("fixture opens"); app.set_option("panel", "pages").unwrap(); app diff --git a/crates/ui-egui/tests/export.rs b/crates/ui-egui/tests/export.rs index f90c0097..4b5a6a77 100644 --- a/crates/ui-egui/tests/export.rs +++ b/crates/ui-egui/tests/export.rs @@ -20,6 +20,7 @@ fn export_dialogs_write_images_and_text() { let d = dir.clone(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, FIXTURE.to_vec()).unwrap(); app.export_dir_override = Some(d.to_string_lossy().into_owned()); app diff --git a/crates/ui-egui/tests/fill_sign.rs b/crates/ui-egui/tests/fill_sign.rs index 54fa780d..7888555e 100644 --- a/crates/ui-egui/tests/fill_sign.rs +++ b/crates/ui-egui/tests/fill_sign.rs @@ -21,6 +21,7 @@ fn harness_bytes(fixture: &[u8]) -> Harness<'static, PdfCraftApp> { let fixture = fixture.to_vec(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, fixture.clone()).unwrap(); app.set_option("left", "closed").unwrap(); app.set_option("zoom", "150").unwrap(); @@ -129,6 +130,7 @@ fn signing_draws_a_signature_once_and_places_it() { // The signature is remembered (persisted with the app's settings). let saved = h.state().persist(); let mut again = PdfCraftApp::new(); + again.set_option("language", "en").unwrap(); again.restore(&saved); assert!(again.signature.is_some()); } @@ -160,6 +162,7 @@ fn typed_signatures_and_initials() { // Both are remembered. let saved = h.state().persist(); let mut again = PdfCraftApp::new(); + again.set_option("language", "en").unwrap(); again.restore(&saved); assert_eq!(again.signature, h.state().signature); assert_eq!(again.initials, Some(pdfcraft_ui_egui::fill_sign::SavedSig::Typed("GH".into()))); @@ -214,6 +217,7 @@ fn changing_saved_signatures_and_initials_preserves_placed_marks() { click(&mut h, 40.0, 100.0); assert_eq!(items(&h).len(), 3); let mut again = PdfCraftApp::new(); + again.set_option("language", "en").unwrap(); again.restore(&h.state().persist()); assert_eq!(again.signature, Some(SavedSig::Typed("Grace Hopper".into()))); assert_eq!(again.initials, Some(SavedSig::Typed("GH".into()))); @@ -269,6 +273,7 @@ fn saved_signature_cards_remove_and_add_without_changing_the_document() { assert_eq!(h.state().signature, None); assert_eq!(h.state().initials, Some(SavedSig::Typed("AL".into()))); let mut again = PdfCraftApp::new(); + again.set_option("language", "en").unwrap(); again.restore(&h.state().persist()); assert_eq!(again.signature, None, "removal survives restart"); h.get_by_label("Add signature").click(); @@ -366,6 +371,7 @@ fn image_signatures_and_initials_can_be_imported_placed_and_remembered() { let settings = state.persist(); assert!(!settings.contains(&path.to_string_lossy().to_string()), "only the image is saved, never its source path"); let mut again = PdfCraftApp::new(); + again.set_option("language", "en").unwrap(); again.restore(&settings); assert_eq!(again.signature, state.signature); assert_eq!(again.initials, state.initials); @@ -800,6 +806,7 @@ fn image_import_cancel_clear_and_errors_preserve_saved_signatures() { assert!(h.state().signature_draft.image.is_none()); // Malformed settings are ignored while legacy typed/drawn values still restore. let mut again = PdfCraftApp::new(); + again.set_option("language", "en").unwrap(); again.restore(r#"{"signature_text":"Ada","signature_image":{"Image":"bm90IGFuIGltYWdl"},"initials":{"Image":"%%%"}}"#); assert_eq!(again.signature, Some(SavedSig::Typed("Ada".into()))); assert!(again.initials.is_none()); diff --git a/crates/ui-egui/tests/folders.rs b/crates/ui-egui/tests/folders.rs index bfc5196d..0f9fe80b 100644 --- a/crates/ui-egui/tests/folders.rs +++ b/crates/ui-egui/tests/folders.rs @@ -51,6 +51,7 @@ impl Drop for TempFolder { fn harness(setup: impl FnOnce(&mut PdfCraftApp) + 'static) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 1400.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); // List folders on the frame that asks, not on a worker thread. app.run_inline = true; setup(&mut app); @@ -144,8 +145,10 @@ fn pinned_folders_survive_a_restart() { let dir = TempFolder::new("restart"); let folder = dir.path(); let mut a = PdfCraftApp::new(); + a.set_option("language", "en").unwrap(); assert!(a.pin_folder(&folder)); let mut b = PdfCraftApp::new(); + b.set_option("language", "en").unwrap(); b.restore(&a.persist()); assert_eq!(b.pinned.folders, [folder]); b.restore("{\"pinned_folders\": [5, \"relative/path\"]}"); @@ -197,6 +200,7 @@ fn show_more_reveals_the_older_pdfs() { #[test] fn pinning_is_limited_and_never_duplicates() { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); assert!(!app.pin_folder("relative/scans"), "a relative path is refused"); let root = if cfg!(windows) { "C:\\" } else { "/" }; let folders: Vec = (0..MAX_PINNED).map(|i| Path::new(root).join(format!("pinned-{i}")).to_string_lossy().into_owned()).collect(); diff --git a/crates/ui-egui/tests/forms.rs b/crates/ui-egui/tests/forms.rs index 1fde4e1e..f4f3552d 100644 --- a/crates/ui-egui/tests/forms.rs +++ b/crates/ui-egui/tests/forms.rs @@ -6,6 +6,7 @@ use pdfcraft_ui_egui::PdfCraftApp; fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, include_bytes!("data/form.pdf").to_vec()).unwrap(); app.set_option("left", "closed").unwrap(); // The whole 300×400 pt page on screen. @@ -148,6 +149,7 @@ fn date_fields_offer_a_calendar() { /// The value of `name` in a saved PDF, read back by opening it in a fresh app. fn saved_value(path: &std::path::Path, name: &str) -> Vec { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("saved.pdf", None, std::fs::read(path).unwrap()).unwrap(); app.session.get(app.views[0].id).unwrap().form.iter().find(|f| f.name == name).unwrap().value.clone() } @@ -310,6 +312,7 @@ fn a_rejected_value_keeps_the_typing_and_stops_the_save() { let out = dir.join("saved.pdf"); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("order.pdf", None, validated_form()).unwrap(); app.set_option("left", "closed").unwrap(); app.set_option("zoom", "150").unwrap(); @@ -356,6 +359,7 @@ fn a_value_refused_on_enter_keeps_save_in_the_same_frame_from_running() { let out = dir.join("saved.pdf"); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("order.pdf", None, validated_form()).unwrap(); app.set_option("left", "closed").unwrap(); app.set_option("zoom", "150").unwrap(); diff --git a/crates/ui-egui/tests/identity.rs b/crates/ui-egui/tests/identity.rs index 87e6a1a7..9a4e3c38 100644 --- a/crates/ui-egui/tests/identity.rs +++ b/crates/ui-egui/tests/identity.rs @@ -21,6 +21,7 @@ trailer << /Root 1 0 R >> fn the_identity_name_signs_new_comments_and_is_remembered() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, PDF.to_vec()).expect("opens"); app.set_option("author", "Tester").unwrap(); app.set_option("dialog", "preferences").unwrap(); @@ -62,6 +63,7 @@ fn the_identity_name_signs_new_comments_and_is_remembered() { assert_eq!(author(&h).as_deref(), Some("Grace Hopper")); // It survives a restart; an empty or missing name keeps the default, and a huge one is cut. let mut restored = PdfCraftApp::new(); + restored.set_option("language", "en").unwrap(); restored.restore(&h.state().persist()); assert_eq!(restored.comment_prefs.author, "Grace Hopper"); restored.restore(r#"{"author": " "}"#); diff --git a/crates/ui-egui/tests/javascript.rs b/crates/ui-egui/tests/javascript.rs index 19ad05a5..a5c5a6af 100644 --- a/crates/ui-egui/tests/javascript.rs +++ b/crates/ui-egui/tests/javascript.rs @@ -33,6 +33,7 @@ fn form() -> Vec { fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, form()).unwrap(); app }); @@ -98,6 +99,7 @@ fn document_scripts_and_preferences() { assert!(!h.state().session.javascript()); let saved = h.state().persist(); let mut fresh = PdfCraftApp::new(); + fresh.set_option("language", "en").unwrap(); fresh.restore(&saved); assert!(!fresh.session.javascript(), "the preference is remembered"); } @@ -108,6 +110,7 @@ fn merge_data_files_into_a_spreadsheet() { std::fs::create_dir_all(&dir).unwrap(); let out = dir.join("report.csv"); let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.save_override = Some(out.to_string_lossy().into_owned()); app.merge_data_files(vec![("form.pdf".into(), form())]); assert_eq!(std::fs::read_to_string(&out).unwrap(), "greeting\n\n"); @@ -121,6 +124,7 @@ fn prepare_a_form_detects_fields_on_a_paper_form() { let paper = pdfcraft_engine::Session::new().create_from_text("t", "Name: ____________________\n\nPhone: ____________________").unwrap().to_vec(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("paper.pdf", None, paper.clone()).unwrap(); app }); diff --git a/crates/ui-egui/tests/layer_actions.rs b/crates/ui-egui/tests/layer_actions.rs index efa9a1e3..95b35ff2 100644 --- a/crates/ui-egui/tests/layer_actions.rs +++ b/crates/ui-egui/tests/layer_actions.rs @@ -38,6 +38,7 @@ fn fixture() -> Vec { fn harness(setup: impl FnOnce(&mut PdfCraftApp) + 'static) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("layers.pdf", None, fixture()).unwrap(); app.set_option("zoom", "150").unwrap(); setup(&mut app); diff --git a/crates/ui-egui/tests/links.rs b/crates/ui-egui/tests/links.rs index 9b5e3215..d3ee7819 100644 --- a/crates/ui-egui/tests/links.rs +++ b/crates/ui-egui/tests/links.rs @@ -9,6 +9,7 @@ use pdfcraft_ui_egui::{Dialog, PdfCraftApp}; fn harness(setup: impl FnOnce(&mut PdfCraftApp) + 'static) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); setup(&mut app); app }); diff --git a/crates/ui-egui/tests/marks.rs b/crates/ui-egui/tests/marks.rs index b19607c4..0a216ba5 100644 --- a/crates/ui-egui/tests/marks.rs +++ b/crates/ui-egui/tests/marks.rs @@ -17,6 +17,7 @@ trailer << /Root 1 0 R >> fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, FIXTURE.to_vec()).unwrap(); app }); diff --git a/crates/ui-egui/tests/ocr.rs b/crates/ui-egui/tests/ocr.rs index 8e9e9774..a62661fd 100644 --- a/crates/ui-egui/tests/ocr.rs +++ b/crates/ui-egui/tests/ocr.rs @@ -19,6 +19,7 @@ fn scan() -> Vec { fn recognize_text_dialog_adds_searchable_text() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("scan.pdf", None, scan()).unwrap(); app.run_inline = true; app @@ -52,6 +53,7 @@ fn recognize_text_in_multiple_files_writes_searchable_copies() { let _ = std::fs::remove_dir_all(&dir); std::fs::create_dir_all(&dir).unwrap(); let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.run_inline = true; app.export_dir_override = Some(dir.to_string_lossy().into_owned()); app.use_files(pdfcraft_ui_egui::FilePurpose::Ocr, vec![("one.pdf".into(), scan()), ("two.pdf".into(), scan())]); diff --git a/crates/ui-egui/tests/pageboxes.rs b/crates/ui-egui/tests/pageboxes.rs index 6ad018b7..865f1277 100644 --- a/crates/ui-egui/tests/pageboxes.rs +++ b/crates/ui-egui/tests/pageboxes.rs @@ -15,6 +15,7 @@ trailer << /Root 1 0 R >> fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, FIXTURE.to_vec()).unwrap(); app.set_option("left", "closed").unwrap(); app.set_option("zoom", "150").unwrap(); diff --git a/crates/ui-egui/tests/pdf_links.rs b/crates/ui-egui/tests/pdf_links.rs index 8095ec10..a149b3d3 100644 --- a/crates/ui-egui/tests/pdf_links.rs +++ b/crates/ui-egui/tests/pdf_links.rs @@ -10,6 +10,7 @@ fn harness() -> Harness<'static, PdfCraftApp> { // 60 fps steps, so two clicks fall inside egui's double-click window. let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).with_step_dt(1.0 / 60.0).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, include_bytes!("data/form.pdf").to_vec()).unwrap(); app.set_option("zoom", "150").unwrap(); app diff --git a/crates/ui-egui/tests/perf.rs b/crates/ui-egui/tests/perf.rs index b4a8e45d..f30be048 100644 --- a/crates/ui-egui/tests/perf.rs +++ b/crates/ui-egui/tests/perf.rs @@ -100,6 +100,7 @@ fn scrolling_a_500_page_document_stays_within_the_frame_budget() { let t0 = std::time::Instant::now(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("big.pdf", None, bytes).expect("opens"); app }); @@ -124,6 +125,7 @@ fn panels_with_hundreds_of_items_stay_within_the_frame_budget() { let b = bytes.clone(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("big.pdf", None, b).expect("opens"); app.set_option("panel", panel).unwrap(); app diff --git a/crates/ui-egui/tests/pickers.rs b/crates/ui-egui/tests/pickers.rs index df8b1ae5..fb0103f4 100644 --- a/crates/ui-egui/tests/pickers.rs +++ b/crates/ui-egui/tests/pickers.rs @@ -45,6 +45,7 @@ fn harness(docs: &[usize]) -> Harness<'static, PdfCraftApp> { let docs = docs.to_vec(); let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); for (i, n) in docs.iter().enumerate() { app.open_bytes(&format!("doc{i}.pdf"), None, fixture(*n)).unwrap(); } diff --git a/crates/ui-egui/tests/prepare.rs b/crates/ui-egui/tests/prepare.rs index 3b1a5dc3..5f011fa8 100644 --- a/crates/ui-egui/tests/prepare.rs +++ b/crates/ui-egui/tests/prepare.rs @@ -9,6 +9,7 @@ use pdfcraft_ui_egui::{PdfCraftApp, QuickTool}; fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, include_bytes!("data/form.pdf").to_vec()).unwrap(); app.set_option("zoom", "150").unwrap(); app diff --git a/crates/ui-egui/tests/print.rs b/crates/ui-egui/tests/print.rs index 94ab37ff..53ad1b86 100644 --- a/crates/ui-egui/tests/print.rs +++ b/crates/ui-egui/tests/print.rs @@ -7,6 +7,7 @@ use pdfcraft_ui_egui::{Dialog, PdfCraftApp}; fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, include_bytes!("data/form.pdf").to_vec()).unwrap(); app }); diff --git a/crates/ui-egui/tests/protect.rs b/crates/ui-egui/tests/protect.rs index 714d5095..903b1510 100644 --- a/crates/ui-egui/tests/protect.rs +++ b/crates/ui-egui/tests/protect.rs @@ -14,6 +14,7 @@ trailer << /Root 1 0 R >> fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("doc.pdf", None, FIXTURE.to_vec()).unwrap(); app }); diff --git a/crates/ui-egui/tests/recovery.rs b/crates/ui-egui/tests/recovery.rs index 6f59bb16..8cc9eebf 100644 --- a/crates/ui-egui/tests/recovery.rs +++ b/crates/ui-egui/tests/recovery.rs @@ -46,6 +46,7 @@ fn files_in(s: &RecoveryStore) -> usize { /// A session that edits a document, autosaves, and then "crashes" (is dropped). fn crashed_session(s: &RecoveryStore, bytes: Vec, password: Option<&str>, path: Option<&str>) { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.enable_recovery(s.clone()); match password { Some(pw) => { @@ -62,6 +63,7 @@ fn crashed_session(s: &RecoveryStore, bytes: Vec, password: Option<&str>, pa fn harness(s: RecoveryStore) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.enable_recovery(s); app }); @@ -103,6 +105,7 @@ fn discarding_removes_the_autosaves() { fn saving_or_closing_clears_the_entry_and_nothing_is_written_for_clean_documents() { let s = store("save"); let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.enable_recovery(s.clone()); app.open_bytes("a.pdf", None, fixture(2)).unwrap(); app.autosave_now(); @@ -130,6 +133,7 @@ fn quitting_cleanly_leaves_nothing_behind() { let s = s.clone(); move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.enable_recovery(s); app.open_bytes("q.pdf", None, fixture(2)).unwrap(); app @@ -206,6 +210,7 @@ fn control_click_effects_are_visible_when_the_reply_arrives() { let (slot2, s2) = (slot.clone(), s.clone()); let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(move |cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); *slot2.lock().unwrap() = Some(app.attach_control(&cc.egui_ctx)); app.enable_recovery(s2); app diff --git a/crates/ui-egui/tests/redact.rs b/crates/ui-egui/tests/redact.rs index b2037788..8351b597 100644 --- a/crates/ui-egui/tests/redact.rs +++ b/crates/ui-egui/tests/redact.rs @@ -9,6 +9,7 @@ use pdfcraft_ui_egui::{Dialog, PdfCraftApp}; fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, include_bytes!("data/form.pdf").to_vec()).unwrap(); app.set_option("zoom", "150").unwrap(); app diff --git a/crates/ui-egui/tests/signing.rs b/crates/ui-egui/tests/signing.rs index 22f82c8d..a777517e 100644 --- a/crates/ui-egui/tests/signing.rs +++ b/crates/ui-egui/tests/signing.rs @@ -30,6 +30,7 @@ fn dir() -> std::path::PathBuf { fn harness(dir: std::path::PathBuf) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("contract.pdf", None, FIXTURE.to_vec()).expect("opens"); app.set_option("left", "closed").unwrap(); app.export_dir_override = Some(dir.to_string_lossy().into_owned()); @@ -155,6 +156,7 @@ fn drawing_a_signature_creating_an_id_signing_and_trusting() { // Trusted certificates and the ID list persist. let saved = s.persist(); let mut fresh = PdfCraftApp::new(); + fresh.set_option("language", "en").unwrap(); fresh.restore(&saved); assert_eq!((fresh.digital_ids.len(), fresh.session.trusted_certificates().len()), (1, 1)); } @@ -248,6 +250,7 @@ fn clicking_an_empty_signature_field_signs_it() { #[test] fn os_store_identities_are_not_persisted() { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); for path in ["windows:Store signer", "keychain:Store signer", "file-id.p12"] { app.digital_ids.push(pdfcraft_ui_egui::DigitalIdEntry { path: path.into(), @@ -262,6 +265,7 @@ fn os_store_identities_are_not_persisted() { assert_eq!(settings["digital_ids"].as_array().unwrap().len(), 1); assert_eq!(settings["digital_ids"][0]["path"], "file-id.p12"); let mut restored = PdfCraftApp::new(); + restored.set_option("language", "en").unwrap(); restored.restore(&saved); assert_eq!(restored.digital_ids.len(), 1); assert_eq!(restored.digital_ids[0].path, "file-id.p12"); diff --git a/crates/ui-egui/tests/stamps.rs b/crates/ui-egui/tests/stamps.rs index 3665f187..ddb24e6a 100644 --- a/crates/ui-egui/tests/stamps.rs +++ b/crates/ui-egui/tests/stamps.rs @@ -8,6 +8,7 @@ use pdfcraft_ui_egui::{PdfCraftApp, QuickTool}; fn choosing_and_placing_stamps() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, include_bytes!("data/form.pdf").to_vec()).unwrap(); app.set_option("zoom", "150").unwrap(); app @@ -47,6 +48,7 @@ fn choosing_and_placing_stamps() { fn creating_placing_and_keeping_custom_stamps() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, include_bytes!("data/form.pdf").to_vec()).unwrap(); app.set_option("zoom", "150").unwrap(); app @@ -88,6 +90,7 @@ fn creating_placing_and_keeping_custom_stamps() { // The library is kept with the app's settings. let saved = s.persist(); let mut again = PdfCraftApp::new(); + again.set_option("language", "en").unwrap(); again.restore(&saved); assert_eq!(again.custom_stamps.len(), 1); assert_eq!((again.custom_stamps[0].category.as_str(), again.custom_stamps[0].name.as_str()), ("Company", "logo")); diff --git a/crates/ui-egui/tests/text_select.rs b/crates/ui-egui/tests/text_select.rs index c2beef92..683f391c 100644 --- a/crates/ui-egui/tests/text_select.rs +++ b/crates/ui-egui/tests/text_select.rs @@ -29,6 +29,7 @@ trailer << /Root 1 0 R >> fn harness(setup: impl FnOnce(&mut PdfCraftApp) + 'static) -> (Harness<'static, PdfCraftApp>, Pos2) { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).with_step_dt(1.0 / 60.0).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("lines.pdf", None, two_lines()).expect("opens"); app.set_option("left", "closed").unwrap(); app.set_option("author", "Tester").unwrap(); diff --git a/crates/ui-egui/tests/theme.rs b/crates/ui-egui/tests/theme.rs index 9a90181f..2b16796f 100644 --- a/crates/ui-egui/tests/theme.rs +++ b/crates/ui-egui/tests/theme.rs @@ -86,13 +86,16 @@ fn toolbar_and_view_menu_share_three_state_preferences() { fn saved_preferences_restore_with_legacy_light_dark_settings() { for (value, preference) in [("light", ThemePreference::Light), ("dark", ThemePreference::Dark), ("system", ThemePreference::System)] { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.set_option("theme", value).unwrap(); let saved = app.persist(); let mut restored = PdfCraftApp::new(); + restored.set_option("language", "en").unwrap(); restored.restore(&saved); assert_eq!(restored.theme_preference, preference); } let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.restore(r#"{"theme":"Dark"}"#); assert_eq!(app.theme_preference, ThemePreference::Dark); assert_eq!(app.theme, ThemeKind::Dark); @@ -107,6 +110,7 @@ fn saved_preferences_restore_with_legacy_light_dark_settings() { #[test] fn commands_and_options_apply_before_the_first_frame() { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.run_command("view.theme.dark"); assert_eq!(app.theme_preference, ThemePreference::Dark); assert_eq!(app.theme, ThemeKind::Dark); diff --git a/crates/ui-egui/tests/ui.rs b/crates/ui-egui/tests/ui.rs index 5299c664..ecc7a21a 100644 --- a/crates/ui-egui/tests/ui.rs +++ b/crates/ui-egui/tests/ui.rs @@ -20,6 +20,7 @@ trailer << /Root 1 0 R >> fn harness(setup: impl FnOnce(&mut PdfCraftApp) + 'static) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); setup(&mut app); app }); @@ -75,6 +76,7 @@ fn a_tab_with_an_arabic_file_name_keeps_its_logical_accessible_name() { #[test] fn garbage_input_is_rejected_without_panicking() { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); assert!(app.open_bytes("junk.pdf", None, b"this is not a pdf".to_vec()).is_err()); assert!(app.open_bytes("empty.pdf", None, Vec::new()).is_err()); let mut truncated = FIXTURE.to_vec(); @@ -173,9 +175,11 @@ fn drag_selects_text_and_copy_returns_it() { #[test] fn persistence_round_trips_and_tolerates_garbage() { let mut a = PdfCraftApp::new(); + a.set_option("language", "en").unwrap(); a.set_option("theme", "dark").unwrap(); let json = a.persist(); let mut b = PdfCraftApp::new(); + b.set_option("language", "en").unwrap(); b.restore(&json); assert_eq!(b.theme, pdfcraft_ui_egui::theme::ThemeKind::Dark); b.restore("{not json"); @@ -246,7 +250,11 @@ impl egui::DroppedFile for Dropped { #[test] fn dropping_a_pdf_on_the_window_opens_it() { - let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(|_cc| PdfCraftApp::new()); + let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(|_cc| { + let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); + app + }); h.run_steps(3); assert!(h.state().views.is_empty()); let file: egui::DroppedFileHandle = std::sync::Arc::new(Dropped { path: "dropped.pdf".into(), bytes: FIXTURE.to_vec() }); @@ -258,7 +266,11 @@ fn dropping_a_pdf_on_the_window_opens_it() { #[test] fn pdfs_dropped_on_the_combine_tab_join_its_list() { - let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(|_cc| PdfCraftApp::new()); + let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(|_cc| { + let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); + app + }); h.run_steps(3); h.state_mut().execute("page.combine"); h.run_steps(2); @@ -284,7 +296,11 @@ fn a_folder_dropped_on_the_combine_tab_adds_its_pdfs() { std::fs::create_dir_all(dir.join("inner")).unwrap(); std::fs::write(dir.join("one.pdf"), FIXTURE).unwrap(); std::fs::write(dir.join("inner").join("two.pdf"), FIXTURE).unwrap(); - let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(|_cc| PdfCraftApp::new()); + let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(|_cc| { + let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); + app + }); h.run_steps(3); h.state_mut().execute("page.combine"); h.run_steps(2); @@ -321,9 +337,11 @@ fn default_workspace_mode_persists_and_tolerates_invalid_settings() { use pdfcraft_ui_egui::Mode; for mode in [Mode::AllTools, Mode::Read, Mode::Edit, Mode::Convert, Mode::Sign] { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.default_mode = mode; app.set_option("mode", "sign").unwrap(); let mut restored = PdfCraftApp::new(); + restored.set_option("language", "en").unwrap(); restored.restore(&app.persist()); assert_eq!(restored.default_mode, mode); restored.open_bytes("fixture.pdf", None, FIXTURE.to_vec()).unwrap(); @@ -331,6 +349,7 @@ fn default_workspace_mode_persists_and_tolerates_invalid_settings() { } for json in ["{}", "{not json", r#"{"default_mode": null}"#, r#"{"default_mode": 5}"#, r#"{"default_mode": "unknown"}"#] { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.restore(json); assert_eq!(app.default_mode, Mode::AllTools); app.open_bytes("fixture.pdf", None, FIXTURE.to_vec()).unwrap(); @@ -342,6 +361,7 @@ fn default_workspace_mode_persists_and_tolerates_invalid_settings() { fn newly_opened_pdfs_use_the_default_workspace() { use pdfcraft_ui_egui::{LeftPanel, Mode}; let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.restore(r#"{"default_mode": "edit"}"#); app.open_bytes("first.pdf", None, FIXTURE.to_vec()).unwrap(); assert_eq!(app.mode, Mode::Edit); @@ -365,6 +385,7 @@ fn explicit_mode_overrides_default_before_and_after_open() { for before in [false, true] { for (value, mode) in [("all", Mode::AllTools), ("read", Mode::Read), ("edit", Mode::Edit), ("convert", Mode::Convert), ("sign", Mode::Sign)] { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.default_mode = Mode::Read; if before { app.set_option("mode", value).unwrap(); @@ -403,6 +424,7 @@ fn preferences_selects_default_workspace_for_next_open() { fn explicit_mode_preserves_independent_tool_and_panel_options() { use pdfcraft_ui_egui::LeftPanel; let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.set_option("tool", "export").unwrap(); app.set_option("left", "closed").unwrap(); app.set_option("mode", "edit").unwrap(); @@ -418,6 +440,7 @@ fn opening_a_pdf_keeps_a_closed_left_panel_and_the_chosen_tool() { use pdfcraft_ui_egui::{LeftPanel, Mode}; // `--left closed` without `--mode`, default workspace Edit: the panel stays closed. let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.default_mode = Mode::Edit; app.set_option("left", "closed").unwrap(); app.open_bytes("fixture.pdf", None, FIXTURE.to_vec()).unwrap(); @@ -425,6 +448,7 @@ fn opening_a_pdf_keeps_a_closed_left_panel_and_the_chosen_tool() { assert!(!app.left_open); // Default All Tools: opening another PDF leaves the tool panel the user picked. let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.set_option("tool", "export").unwrap(); app.open_bytes("first.pdf", None, FIXTURE.to_vec()).unwrap(); app.open_bytes("second.pdf", None, FIXTURE.to_vec()).unwrap(); diff --git a/crates/ui-egui/tests/updates.rs b/crates/ui-egui/tests/updates.rs index 141c2781..c06bd8b2 100644 --- a/crates/ui-egui/tests/updates.rs +++ b/crates/ui-egui/tests/updates.rs @@ -15,6 +15,7 @@ fn source(answer: Result<&str, &str>) -> UpdateSource { fn harness(answer: Result<&str, &str>) -> Harness<'static, PdfCraftApp> { Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.update_source = Some(source(answer)); app }) @@ -78,6 +79,7 @@ fn nothing_is_asked_until_the_user_checks() { let counted = calls.clone(); let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); // Settings from a build that had the startup option are ignored. app.restore(r#"{"check_updates_at_start": true}"#); let counted = counted.clone(); diff --git a/crates/ui-egui/tests/view.rs b/crates/ui-egui/tests/view.rs index aedeff82..2d8881ea 100644 --- a/crates/ui-egui/tests/view.rs +++ b/crates/ui-egui/tests/view.rs @@ -35,6 +35,7 @@ fn harness(options: &'static [(&'static str, &'static str)]) -> Harness<'static, fn harness_stepping(step_dt: f32, options: &'static [(&'static str, &'static str)]) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).with_step_dt(step_dt).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("pages.pdf", None, PAGES.to_vec()).expect("opens"); app.set_option("left", "closed").unwrap(); app.set_option("panel", "none").unwrap(); @@ -63,6 +64,7 @@ trailer << /Root 1 0 R >> fn mixed_harness(options: &'static [(&'static str, &'static str)]) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("mixed.pdf", None, MIXED.to_vec()).expect("opens"); app.set_option("left", "closed").unwrap(); app.set_option("panel", "none").unwrap(); @@ -230,6 +232,7 @@ fn layout_option_rejects_typos() { #[test] fn view_options_without_a_document() { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); assert!(app.set_option("default-layout", "single").is_ok(), "the default needs no document"); assert_eq!(app.view_defaults.layout, pdfcraft_ui_egui::canvas::PageLayout::Single); assert!(app.set_option("cover", "on").is_err(), "cover needs an open document"); @@ -251,6 +254,7 @@ fn default_page_display_is_used_for_new_documents() { // Persisted preferences survive a restart. let saved = h.state().persist(); let mut fresh = PdfCraftApp::new(); + fresh.set_option("language", "en").unwrap(); fresh.restore(&saved); assert_eq!(fresh.view_defaults.layout, PageLayout::Single); // Garbage keeps the previous default; casing is forgiven; legacy files lack the key. @@ -259,6 +263,7 @@ fn default_page_display_is_used_for_new_documents() { fresh.restore(r#"{"default_layout":"TWO-UP"}"#); assert_eq!(fresh.view_defaults.layout, PageLayout::TwoUp); let mut legacy = PdfCraftApp::new(); + legacy.set_option("language", "en").unwrap(); legacy.restore("{}"); assert_eq!(legacy.view_defaults.layout, PageLayout::Continuous); } @@ -287,6 +292,7 @@ fn default_zoom_is_used_for_new_documents() { // Saved settings keep it, and garbage leaves it alone. let saved = h.state().persist(); let mut fresh = PdfCraftApp::new(); + fresh.set_option("language", "en").unwrap(); fresh.restore(&saved); fresh.restore(r#"{"default_zoom":"bogus"}"#); fresh.open_bytes("again.pdf", None, PAGES.to_vec()).expect("opens"); @@ -463,6 +469,7 @@ trailer << /Root 1 0 R >> fn form_harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, FORM.to_vec()).expect("opens"); app.set_option("left", "closed").unwrap(); app @@ -531,6 +538,7 @@ fn required_radio_buttons_get_a_round_red_border() { let _gpu = gpu(); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("radios.pdf", None, RADIOS.to_vec()).expect("opens"); app.set_option("left", "closed").unwrap(); app.set_option("panel", "none").unwrap(); @@ -575,6 +583,7 @@ fn field_highlighting_is_remembered() { assert!(h.state().views[1].highlight_fields, "the next document opens highlighted"); let saved = h.state().persist(); let mut next = PdfCraftApp::new(); + next.set_option("language", "en").unwrap(); next.restore(&saved); next.open_bytes("form.pdf", None, FORM.to_vec()).expect("opens"); assert!(next.views[0].highlight_fields, "and so does the next session"); @@ -666,6 +675,7 @@ trailer << /Root 1 0 R >> let bytes = pdf.clone(); let mut h = Harness::builder().with_size(egui::vec2(1000.0, 800.0)).with_pixels_per_point(ppp).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("lines.pdf", None, bytes.clone()).expect("opens"); app.set_option("left", "closed").unwrap(); app.set_option("panel", "none").unwrap(); diff --git a/crates/ui-egui/tests/viewer.rs b/crates/ui-egui/tests/viewer.rs index 39802d6f..67dcecb2 100644 --- a/crates/ui-egui/tests/viewer.rs +++ b/crates/ui-egui/tests/viewer.rs @@ -34,6 +34,7 @@ fn fixture(n: usize) -> Vec { fn harness() -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("a.pdf", None, fixture(5)).unwrap(); app.open_bytes("b.pdf", None, fixture(2)).unwrap(); app @@ -213,6 +214,7 @@ trailer << /Root 1 0 R >> .to_vec(); let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("damaged.pdf", None, damaged.clone()).unwrap(); app }); @@ -250,6 +252,7 @@ fn initial_view_is_edited_and_honoured_on_open() { // Opening the saved file follows it. let bytes = s.session.save_bytes(id).unwrap(); let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("again.pdf", None, bytes.to_vec()).unwrap(); let view = &app.views[0]; assert_eq!((view.current, view.cover, app.right), (1, true, Some(pdfcraft_ui_egui::RightPanel::Bookmarks))); @@ -559,6 +562,7 @@ fn page_down_steps_every_spread_when_several_fit_on_screen() { let bytes = sized_fixture(&sizes); let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("book.pdf", None, bytes).unwrap(); app }); diff --git a/crates/ui-egui/tests/xfa.rs b/crates/ui-egui/tests/xfa.rs index f03d12c4..587972d5 100644 --- a/crates/ui-egui/tests/xfa.rs +++ b/crates/ui-egui/tests/xfa.rs @@ -36,6 +36,7 @@ const XFA_PACKET: &str = "<< /Length 52 >>\nstream\n) -> Harness<'static, PdfCraftApp> { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 800.0)).build_eframe(move |_cc| { let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); app.open_bytes("form.pdf", None, bytes).expect("opens"); app });