#![cfg(feature = "javascript")]
use pdfrum::{Cascade, Document, FieldRef, FormSession, Modifiers, Point, ScriptConfig};
const FIXTURE: &str = "tests/fixtures/public_methods.pdf";
const INSIDE: Point = Point::new(150.0, 175.0);
fn click(session: &mut FormSession<'_>) {
session.mouse_move(0, INSIDE, Modifiers::NONE);
session.mouse_down(0, INSIDE, Modifiers::NONE);
session.mouse_up(0, INSIDE, Modifiers::NONE);
}
#[test]
fn typing_runs_the_documents_own_keystroke_script() {
let doc = Document::open(FIXTURE).expect("the public_methods fixture must open");
let mut session = FormSession::with_scripts(&doc, &ScriptConfig::frozen_at(1_399_672_130))
.expect("boa builds a realm on any input");
click(&mut session);
assert!(
session.focused_annot().is_some(),
"the click must land on the widget, or nothing below is testing a script"
);
let after_load = session
.scripts()
.expect("a scripted session")
.transcript_text();
assert!(
after_load.starts_with("Alert: *** starting test 1 ***\n"),
"loading the page runs the formatter; got {after_load:?}"
);
assert!(
!after_load.contains("*** starting test 2 ***"),
"the keystroke script must not have run yet; got {after_load:?}"
);
session.character('7', Modifiers::NONE);
let transcript = session
.scripts()
.expect("a scripted session")
.transcript_text();
assert!(
transcript.contains("Alert: *** starting test 2 ***\n"),
"the field's own /AA /K script ran and asked the host to alert; got {transcript:?}"
);
}
#[test]
fn committing_runs_the_rest_of_the_cascade() {
let doc = Document::open(FIXTURE).expect("the public_methods fixture must open");
let mut session = FormSession::with_scripts(&doc, &ScriptConfig::frozen_at(1_399_672_130))
.expect("boa builds a realm on any input");
click(&mut session);
session.character('7', Modifiers::NONE);
let text = session
.scripts()
.expect("a scripted session")
.transcript_text();
assert!(
text.contains("Alert: *** ending test 2 ***"),
"the keystroke hook ran its script to the end; got {} lines",
text.lines().count()
);
let committed = session.blur();
assert!(committed.consumed);
let text = session
.scripts()
.expect("a scripted session")
.transcript_text();
assert!(
text.lines().any(|line| line.contains("PASS:")),
"the AF* library the fixture tests answered at least once"
);
assert_eq!(
session.scripts().expect("a scripted session").stops(),
[],
"no script stopped on a limit or an uncaught throw"
);
}
#[test]
fn the_default_session_runs_no_script_on_the_same_fixture() {
let doc = Document::open(FIXTURE).expect("the public_methods fixture must open");
let mut session = FormSession::new(&doc);
click(&mut session);
session.character('7', Modifiers::NONE);
session.blur();
assert!(
session.scripts().is_none(),
"a default session has no engine to ask"
);
}
#[test]
fn a_callers_own_cascade_gates_the_commit() {
struct RefuseAll {
asked: std::cell::Cell<u32>,
}
impl Cascade for RefuseAll {
fn validate(&mut self, _field: &FieldRef, _value: &str) -> bool {
self.asked.set(self.asked.get() + 1);
false
}
}
let doc = Document::open(FIXTURE).expect("the public_methods fixture must open");
let mut session = FormSession::with_cascade(
&doc,
RefuseAll {
asked: std::cell::Cell::new(0),
},
);
click(&mut session);
session.character('7', Modifiers::NONE);
assert_eq!(session.focused_text().as_deref(), Some("7"));
session.blur();
assert_eq!(session.focused_text().as_deref(), Some(""));
assert!(
session.focused_annot().is_some(),
"a refused commit keeps the caret in the field"
);
}
#[test]
#[cfg(feature = "javascript")]
fn a_timer_fires_only_when_the_host_advances_the_clock() {
use std::time::Duration;
let doc = Document::open(FIXTURE).expect("the public_methods fixture must open");
let mut session = FormSession::with_scripts(&doc, &ScriptConfig::frozen_at(1_399_672_130))
.expect("boa builds a realm on any input");
let scripts = session
.scripts_mut()
.expect("a scripted session has an engine");
assert!(scripts.run("app.setInterval(\"app.alert('tick')\", 1000);", "test"));
let before = scripts.transcript().len();
assert_eq!(
session
.scripts()
.expect("a scripted session")
.transcript()
.len(),
before,
"arming a timer runs nothing on its own"
);
assert_eq!(session.advance_time(Duration::from_secs(1)), 1);
let after = session
.scripts()
.expect("a scripted session")
.transcript_text();
assert!(after.ends_with("Alert: tick\n"), "got {after:?}");
}
#[test]
#[cfg(feature = "javascript")]
fn advancing_a_script_free_session_is_zero_and_not_a_panic() {
use std::time::Duration;
let doc = Document::open(FIXTURE).expect("the public_methods fixture must open");
let mut session = FormSession::new(&doc);
assert_eq!(session.advance_time(Duration::from_hours(1)), 0);
}
#[test]
fn the_facade_installs_what_the_document_object_answers_from() {
let doc = Document::open(FIXTURE).expect("the public_methods fixture must open");
let mut session = FormSession::with_scripts(&doc, &ScriptConfig::frozen_at(1_399_672_130))
.expect("boa builds a realm on any input");
let scripts = session
.scripts_mut()
.expect("a scripted session has an engine");
assert!(
scripts.run(
"app.alert('numFields=' + this.numFields);\
app.alert('numPages=' + this.numPages);\
app.alert('Text3=' + this.getField('Text3').value);\
app.alert('name=' + this.getField('Text3').name);\
app.alert('nth=' + this.getNthFieldName(0));",
"test",
),
"the script must complete: {:?}",
scripts.stops()
);
let text = scripts.transcript_text();
for expected in [
"Alert: numFields=4",
"Alert: numPages=1",
"Alert: Text3=456",
"Alert: name=Text3",
"Alert: nth=Text Box",
] {
assert!(
text.contains(expected),
"the model must answer {expected:?}; got {text:?}"
);
}
}
#[test]
fn a_document_with_no_info_dictionary_throws_from_its_metadata() {
let doc = Document::open(FIXTURE).expect("the public_methods fixture must open");
let mut session = FormSession::with_scripts(&doc, &ScriptConfig::frozen_at(1_399_672_130))
.expect("boa builds a realm on any input");
let scripts = session
.scripts_mut()
.expect("a scripted session has an engine");
assert!(
scripts.run(
"try { app.alert('author=' + this.author); }\
catch (e) { app.alert('threw: ' + e); }",
"test",
),
"the script must complete: {:?}",
scripts.stops()
);
let text = scripts.transcript_text();
assert!(
text.contains("Alert: threw: "),
"no /Info means the getter throws rather than answering; got {text:?}"
);
}
#[test]
fn a_cascade_nobody_told_about_a_document_answers_as_an_empty_one() {
let doc = Document::open(FIXTURE).expect("the public_methods fixture must open");
let mut cascade = pdfrum::ScriptCascade::new(&ScriptConfig::frozen_at(1_399_672_130))
.expect("boa builds a realm on any input");
assert!(
cascade.run(
"app.alert('numFields=' + this.numFields);\
app.alert('field=' + this.getField('Text3'));",
"test",
),
"the script must complete: {:?}",
cascade.stops()
);
let text = cascade.transcript_text();
assert!(
text.contains("Alert: numFields=0"),
"an uninstalled model counts no fields; got {text:?}"
);
assert!(
text.contains("Alert: field=undefined"),
"and finds none by name; got {text:?}"
);
let session = FormSession::with_cascade(&doc, cascade);
assert!(
session.scripts().is_none(),
"`with_cascade` holds a plain cascade, whatever was passed to it"
);
}