mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 03:27:21 -06:00
Merge upstream main 924c4da4a into next
This commit is contained in:
@@ -11,7 +11,17 @@ fn result(auto: &mut Automation, args: Value) -> Value {
|
||||
|
||||
// Fresh fixtures include the current timestamp; retain their bytes for output comparisons.
|
||||
fn pdf() -> Vec<u8> {
|
||||
Session::new().create_from_text("Original", "Original text").unwrap().as_ref().clone()
|
||||
// Reuse one synthetic document so byte comparisons cannot straddle the
|
||||
// writer's modification-time clock. Each caller gets an independent buffer.
|
||||
static PDF: std::sync::OnceLock<Vec<u8>> = std::sync::OnceLock::new();
|
||||
PDF.get_or_init(|| Session::new().create_from_text("Original", "Original text").unwrap().as_ref().clone()).clone()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pdf_fixture_remains_identical_across_metadata_clock_tick() {
|
||||
let before = pdf();
|
||||
std::thread::sleep(std::time::Duration::from_millis(1100));
|
||||
assert_eq!(before, pdf());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -506,7 +506,8 @@ mod cups_options {
|
||||
let Some(record) = std::env::var_os(RECORD) else { return };
|
||||
let args: Vec<String> = std::env::args().skip_while(|a| a != "--").skip(1).collect();
|
||||
std::fs::write(std::path::Path::new(&record).join("args"), args.join("\n")).unwrap();
|
||||
println!("{}", std::env::var(ANSWER).unwrap());
|
||||
// Serial libtest prints its test label without a newline before this helper's stdout.
|
||||
println!("\n{}", std::env::var(ANSWER).unwrap());
|
||||
}
|
||||
|
||||
fn command(record: &std::path::Path, answer: &str) -> std::process::Command {
|
||||
|
||||
@@ -608,3 +608,65 @@ fn tool_picker_keeps_picked_recipient_when_other_open_is_ready() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::expect_used, clippy::unwrap_used)]
|
||||
fn inline_tool_open_keeps_its_command_for_pdf_conversion_and_password_retry() {
|
||||
for (name, bytes, encrypted) in
|
||||
[("plain.pdf", fixture(1), false), ("converted.txt", b"Text to export".to_vec(), false), ("locked.pdf", encrypted_tool_fixture(), true)]
|
||||
{
|
||||
let path = std::env::temp_dir().join(format!("pdfcraft-inline-tool-{}-{name}", std::process::id()));
|
||||
std::fs::write(&path, bytes).expect("write selected file");
|
||||
let mut app = PdfCraftApp::new();
|
||||
app.set_option("language", "en").expect("language preference");
|
||||
assert!(app.ctx.is_none(), "this exercises the no-context inline path");
|
||||
app.open_path_then(&path.to_string_lossy(), Some("export.text"));
|
||||
if encrypted {
|
||||
assert!(app.dialog.is_none());
|
||||
assert_eq!(app.password_prompt.as_ref().expect("password prompt").then, Some("export.text"));
|
||||
app.submit_password(Some("wrong".into()));
|
||||
assert!(app.password_prompt.as_ref().expect("wrong-password prompt").error.is_some());
|
||||
assert!(app.dialog.is_none());
|
||||
app.submit_password(Some("tool-password".into()));
|
||||
}
|
||||
assert_eq!(app.views.len(), 1);
|
||||
assert_eq!(app.active, Some(0));
|
||||
assert!(matches!(app.dialog, Some(crate::Dialog::Export(_))), "the inline {name} retains its export command");
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
|
||||
fn startup_files_wait_for_absolute_link_consent_and_preparation() {
|
||||
let (mut h, _) = harness();
|
||||
let source = h.state().views[0].id;
|
||||
let path = std::env::temp_dir().join(format!("pdfcraft-open-consented-startup-{}.pdf", std::process::id()));
|
||||
std::fs::write(&path, fixture(2)).expect("write linked PDF");
|
||||
h.state_mut().open_remote_link(source, path.to_string_lossy().into_owned(), pdfcraft_render::RemoteDestination::Default);
|
||||
h.state().startup_inbox.lock().expect("startup queue").push(("startup.pdf".into(), fixture(1)));
|
||||
h.step();
|
||||
assert!(h.state().pending_remote_link.is_some());
|
||||
assert_eq!(h.state().startup_inbox.lock().expect("startup queue").len(), 1);
|
||||
assert_eq!(h.state().views.len(), 1, "startup cannot replace the consent interaction");
|
||||
|
||||
let (gate, started, release, finished) = open_worker::prepare_gate();
|
||||
h.state_mut().open_prepare_gate = Some(gate);
|
||||
h.state_mut().resolve_pending_remote_link(true);
|
||||
started.recv_timeout(Duration::from_secs(5)).expect("worker reached prepare barrier");
|
||||
h.step();
|
||||
let queued = h.state().startup_inbox.lock().expect("startup queue").len();
|
||||
let visible = h.state().views.len();
|
||||
// Always release before asserting: a failure must not strand the worker at the barrier.
|
||||
release.send(()).expect("release prepare barrier");
|
||||
finished.recv_timeout(Duration::from_secs(5)).expect("worker finished preparation");
|
||||
assert_eq!(queued, 1, "startup bytes wait while the approved linked PDF prepares");
|
||||
assert_eq!(visible, 1);
|
||||
h.step();
|
||||
let app = h.state();
|
||||
assert_eq!(app.views.len(), 3);
|
||||
assert_eq!(app.session.get(app.views[1].id).expect("linked document").path.as_deref(), path.to_str());
|
||||
assert_eq!(app.session.get(app.views[2].id).expect("startup document").name, "startup.pdf");
|
||||
assert_eq!(app.active, Some(1), "the user's approved navigation keeps focus");
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user