fix(plot): never fail silently on PDF export; add EXPORTPDF <path> (#369)
Export to PDF could end in complete silence: the flow hands off to an async rfd save dialog, and when that future resolves to None the app mapped it straight to Task::none(). None does not only mean the user cancelled — on Linux the XDG portal SaveFile call can fail outright (rfd then tries a zenity fallback, which may not be installed) and the future still just resolves to None. rfd reports those failures only through the `log` crate, and logging is opt-in (--log / RUST_LOG), so the reporter saw exactly what #369 describes: no dialog, no error, no console output, no file. - EXPORT / EXPORTPDF now accept an optional path argument that exports straight to the file with no dialog — both the workaround for systems where the native save dialog cannot open and a headless-testable entry point. A relative path lands next to the drawing; a missing extension gets .pdf. - The dialog-None arms (PlotExportPath / PlotWindowExportPath) now push a command-line message pointing at EXPORTPDF <path> instead of doing nothing. - The success message prints the full output path, not just the file name, so users can find the PDF. Verified end to end on the real binary via --serve: LINE + CIRCLE then EXPORTPDF <path> writes a valid PDF with the expected geometry. Both new tests fail on the previous code. Fixes #369 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4bcf5063e9
commit
d2af4c0b34
5 changed files with 116 additions and 11 deletions
|
|
@ -541,6 +541,56 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exportpdf_with_explicit_path_writes_pdf_without_dialog() {
|
||||
// EXPORTPDF <path> must export straight to the given file — no save
|
||||
// dialog — and confirm on the command line. This is the dialog-free
|
||||
// export path added for #369 (Export to PDF silently did nothing when
|
||||
// the native save dialog could not open).
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
app.automation_op(r#"{"op":"run","cmd":"LINE 0,0 100,50"}"#);
|
||||
|
||||
let path = std::env::temp_dir().join(format!("ocs_test_369_{}.pdf", std::process::id()));
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let start = app.command_line.history.len();
|
||||
let _ = app.run_command_line(&format!("EXPORTPDF {}", path.display()));
|
||||
let out: String = app.command_line.history[start..]
|
||||
.iter()
|
||||
.map(|e| e.text.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(out.contains("Exported"), "no export confirmation: {out:?}");
|
||||
|
||||
let bytes = std::fs::read(&path).expect("EXPORTPDF <path> should write the file");
|
||||
assert!(bytes.starts_with(b"%PDF"), "output is not a PDF");
|
||||
assert!(bytes.len() > 200, "suspiciously small PDF: {}", bytes.len());
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancelled_pdf_save_dialog_reports_on_command_line() {
|
||||
// The save dialog resolving to None (cancelled, or it never opened —
|
||||
// e.g. a broken XDG portal) must leave a visible message, not silence.
|
||||
// Regression for #369.
|
||||
use crate::app::Message;
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
for msg in [Message::PlotExportPath(None), Message::PlotWindowExportPath(None)] {
|
||||
let start = app.command_line.history.len();
|
||||
let _ = app.update(msg);
|
||||
let out: String = app.command_line.history[start..]
|
||||
.iter()
|
||||
.map(|e| e.text.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(
|
||||
out.contains("EXPORTPDF"),
|
||||
"dialog-None must mention the EXPORTPDF fallback: {out:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_then_open_round_trips() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
|
|
|
|||
Loading…
Reference in a new issue