#![allow(clippy::expect_used)]
use pdfrum::{Document, FormSession, Key, Modifiers, SessionConfig, Subtype};
fn document() -> Document {
Document::open("tests/fixtures/annots_action_handling.pdf")
.expect("the action-handling fixture must open")
}
fn session_with_links(doc: &Document) -> FormSession<'_> {
FormSession::with_config(
doc,
SessionConfig {
focusable: vec![Subtype::Widget, Subtype::Link],
..SessionConfig::default()
},
)
}
#[test]
fn a_focused_link_fires_its_action_on_return_with_the_modifiers_held() {
let doc = document();
let mut session = session_with_links(&doc);
let mut fired = Vec::new();
for _ in 0..12 {
if !session.key_down(Key::Tab, Modifiers::NONE).consumed {
break;
}
let response = session.key_down(Key::Return, Modifiers::NONE);
if response.actions().count() > 0 {
fired.push(response);
break;
}
}
assert!(
!fired.is_empty(),
"tabbing to a link and pressing Return must request its action"
);
let mut seen = Vec::new();
for modifiers in [
Modifiers::NONE,
Modifiers::CONTROL,
Modifiers::SHIFT,
Modifiers::SHIFT.union(Modifiers::CONTROL),
] {
let response = session.key_down(Key::Return, modifiers);
for (_, held) in response.actions() {
seen.push(held.bits());
}
}
assert_eq!(
seen,
vec![0, 2, 1, 3],
"each request carries the modifiers that were held, in order"
);
}
#[test]
fn no_key_but_return_fires_a_links_action() {
let doc = document();
let mut session = session_with_links(&doc);
let mut reached = false;
for _ in 0..12 {
if !session.key_down(Key::Tab, Modifiers::NONE).consumed {
break;
}
if session
.key_down(Key::Return, Modifiers::NONE)
.actions()
.count()
> 0
{
reached = true;
break;
}
}
assert!(reached, "a link must be reachable by tabbing");
for key in [Key::Space, Key::Tab, Key::A, Key::Left, Key::Delete] {
let response = session.key_down(key, Modifiers::NONE);
assert_eq!(
response.actions().count(),
0,
"{key:?} must not fire a link's action"
);
if key == Key::Tab {
session.key_down(Key::Tab, Modifiers::SHIFT);
}
}
}
#[test]
fn links_are_not_focusable_by_default() {
fn reachable(session: &mut FormSession<'_>) -> usize {
let mut fired = 0;
for _ in 0..12 {
if !session.key_down(Key::Tab, Modifiers::NONE).consumed {
break;
}
fired += session
.key_down(Key::Return, Modifiers::NONE)
.actions()
.count();
}
fired
}
let doc = document();
let default_ring = reachable(&mut FormSession::new(&doc));
let with_links = reachable(&mut session_with_links(&doc));
assert_eq!(
default_ring, 1,
"a default session's ring holds widgets alone — the button is the one"
);
assert!(
with_links > default_ring,
"admitting links to the ring puts a link's action in reach \
(default {default_ring}, with links {with_links})"
);
}
#[test]
fn a_push_buttons_return_fires_its_action() {
let doc = document();
let mut session = FormSession::new(&doc);
session.key_down(Key::Tab, Modifiers::NONE);
session.key_down(Key::Tab, Modifiers::NONE);
let response = session.character('\r', Modifiers::NONE);
assert_eq!(
response.actions().count(),
1,
"a push button's Return fires its action (crbug.com/1028991)"
);
let response = session.key_down(Key::Return, Modifiers::NONE);
assert_eq!(
response.actions().count(),
1,
"the key path fires the same action as the char path"
);
}
#[test]
fn an_annotation_with_no_action_fires_nothing() {
let doc = document();
let mut session = session_with_links(&doc);
for _ in 0..12 {
if !session.key_down(Key::Tab, Modifiers::NONE).consumed {
break;
}
let _ = session.key_down(Key::Return, Modifiers::NONE);
}
}