Phase 1 of NIGIG_PDF_FEATURE_PARITY_PLAN.md. Findings verified by running them, written up in REVIEWS/PDF_PARITY_PHASE1_STATUS.md. Three of the five exit criteria are met: the workspace is green on the new rev (pdf 606 passing), both pins are already at 5efe6e24c, and the upstream baseline is documented - libs/pdf_parse is 4,575 lines with no save/write path at all, against nigig-pdf's 25,845 lines with writing, encryption, signatures, structure tree and transparency. nigig-pdf supersedes both libs/pdf_parse and widgets/src/pdf_view.rs; nothing in either is a capability we lack. The remaining two criteria need fork work this repo cannot do. WHY THE UI SUITE CANNOT PASS YET The six #[ignore] markers were removed from pdf-makepad/tests/ui.rs and the docs now claim the suite runs without a Studio hub. The markers went but the tests did not start passing - TEST_TARGET=pdf-ui was simply red. Three layers, each found by fixing the one in front of it: 1. studio/hub/src/build_manager.rs:398 spawns the build with `sh -lc`. The -l makes it a LOGIN shell, which discards the inherited PATH and rebuilds it from /etc/profile, where ~/.cargo/bin does not appear. cargo is not found and the child exits 127 in 0.4s. This breaks any rustup-based CI, not just this sandbox. `sh -c`, or resolving cargo through the CARGO env var, would fix it. 2. Past that the build runs (88s) and the failure becomes 101. libs/makepad_test sets MAKEPAD=headless for the child, but platform/src/os/linux/windowing_backend.rs only knows X11 and Wayland - there is no headless backend and the env var is not consulted. The app selects X11, finds no display, and segfaults (139). This is the real Phase 1 fork task: "terminal/standalone mode" needs a backend, not just an env var the harness sets. 3. Under xvfb-run the app starts properly and OpenGL initialises, so the binary is fine - but the hub spawns its child outside that display. The markers are restored, with a reason pointing at the status document. A red suite everyone knows to disregard stops reporting the next real regression, which is strictly worse than an explicit skip. Also fixes a latent build break this exposed: the fork's app_main! macro expands to #[cfg(native_activity)], a cfg this crate never declares, which is a hard error under -D warnings. Declared as expected-but-unset via [lints.rust] check-cfg rather than silencing unexpected_cfgs wholesale, which would also hide our own typos. One genuine improvement on this rev: pdf-makepad now builds in release inside the workspace. That was previously blocked by a Makepad os::linux feature-unification bug. TEST_TARGET=pdf 606 passing, TEST_TARGET=pdf-ui 651 passing + 6 ignored.
173 lines
6.7 KiB
Rust
173 lines
6.7 KiB
Rust
//! UI tests driving the PDF widget through real Makepad event delivery.
|
|
//!
|
|
//! This closes the gap carried since Phase 4: the interaction logic behind
|
|
//! `handle_event` was unit-tested, but nothing proved a click actually
|
|
//! reaches it through `event.hits()`. These tests go through the headless
|
|
//! `makepad_test` runtime against the test host in `src/bin/nigig-pdf-makepad.rs`,
|
|
//! so the whole path — event delivery, hit testing, action emission — is
|
|
//! exercised without a Studio hub.
|
|
//!
|
|
//! Run with `--test-threads=1`: each test starts its own app instance, and
|
|
//! several competing for the same port produces spurious failures rather
|
|
//! than real ones.
|
|
//!
|
|
//! **Currently `#[ignore]`d.** The fork's `makepad_test` sets
|
|
//! `MAKEPAD=headless` for the app it spawns, but
|
|
//! `platform/src/os/linux/windowing_backend.rs` still only knows `X11` and
|
|
//! `Wayland` — there is no headless backend, so the app selects X11, finds
|
|
//! no display and dies. The full three-layer diagnosis, including a
|
|
//! `sh -lc` bug in the hub that hides `cargo` from the build, is in
|
|
//! `REVIEWS/PDF_PARITY_PHASE1_STATUS.md`.
|
|
//!
|
|
//! These are ignored rather than deleted or left failing on purpose: a red
|
|
//! suite everyone knows to disregard stops reporting the next real
|
|
//! regression. Remove the markers when the fork grows a headless backend.
|
|
//!
|
|
//! ```bash
|
|
//! cargo test -p nigig-pdf-makepad --test ui
|
|
//! ```
|
|
//!
|
|
//! The framework's `click()` targets a widget's centre, which is no use for
|
|
//! hitting a specific annotation, so coordinate clicks are sent as raw
|
|
//! mouse events through `forward()`.
|
|
|
|
use makepad_widgets::makepad_test::{makepad_test, Selector, StudioToApp, TestApp};
|
|
// The Remote* payload structs are not re-exported by makepad_test, so they
|
|
// come from the protocol crate the widget crate already depends on.
|
|
use makepad_widgets::makepad_platform::studio::{
|
|
RemoteKeyModifiers, RemoteMouseDown, RemoteMouseUp,
|
|
};
|
|
|
|
/// Window geometry declared by the test host.
|
|
const WINDOW_WIDTH: f64 = 800.0;
|
|
/// The fixture page is 792pt tall and the widget fills the window below the
|
|
/// action label, so PDF y maps to screen y by this offset. The label occupies
|
|
/// 28px (verified from the widget's live viewport origin during bring-up).
|
|
const LABEL_HEIGHT: f64 = 28.0;
|
|
const PAGE_HEIGHT: f64 = 792.0;
|
|
|
|
/// Convert a PDF-space point on the fixture page into a window coordinate.
|
|
///
|
|
/// PDF y grows upward from the bottom-left; the window grows downward from
|
|
/// the top-left, and the widget starts below the action label.
|
|
fn pdf_to_window(x: f64, y: f64) -> (f64, f64) {
|
|
(x, LABEL_HEIGHT + (PAGE_HEIGHT - y))
|
|
}
|
|
|
|
/// Send a press-and-release at a window coordinate.
|
|
fn click_at(app: &TestApp, x: f64, y: f64) {
|
|
let time = 0.0;
|
|
app.forward(vec![
|
|
StudioToApp::MouseDown(RemoteMouseDown {
|
|
button_raw_bits: 0,
|
|
x,
|
|
y,
|
|
time,
|
|
modifiers: RemoteKeyModifiers::default(),
|
|
}),
|
|
StudioToApp::MouseUp(RemoteMouseUp {
|
|
button_raw_bits: 0,
|
|
x,
|
|
y,
|
|
time,
|
|
modifiers: RemoteKeyModifiers::default(),
|
|
}),
|
|
]);
|
|
}
|
|
|
|
/// Click a point given in the fixture's PDF coordinates.
|
|
fn click_pdf_point(app: &TestApp, x: f64, y: f64) {
|
|
let (wx, wy) = pdf_to_window(x, y);
|
|
click_at(app, wx, wy);
|
|
}
|
|
|
|
/// The widget mounts and draws without panicking.
|
|
///
|
|
/// The weakest possible assertion, and still worth making: it is the first
|
|
/// thing proving the widget survives a real draw pass with a parsed
|
|
/// document, which no unit test can show.
|
|
#[makepad_test]
|
|
#[ignore = "needs a Makepad headless backend; see REVIEWS/PDF_PARITY_PHASE1_STATUS.md"]
|
|
fn the_pdf_widget_mounts_and_draws(app: TestApp) {
|
|
app.locator(Selector::id("pdf_page")).wait_visible();
|
|
}
|
|
|
|
/// The host starts with no action reported, so a later assertion that an
|
|
/// action appeared cannot pass vacuously.
|
|
#[makepad_test]
|
|
#[ignore = "needs a Makepad headless backend; see REVIEWS/PDF_PARITY_PHASE1_STATUS.md"]
|
|
fn no_action_is_reported_before_any_input(app: TestApp) {
|
|
app.locator(Selector::id("pdf_page")).wait_visible();
|
|
app.locator(Selector::id("action_label"))
|
|
.wait_visible()
|
|
.assert_text("no action");
|
|
}
|
|
|
|
/// The Phase 4 exit criterion, now through real event delivery rather than
|
|
/// a direct call into `InteractionState`.
|
|
///
|
|
/// The fixture's link occupies PDF-space 100,500 to 200,520 on page index 1.
|
|
/// Clicking it must surface `OpenUri` on the host, which proves the action
|
|
/// travelled the whole path instead of stopping inside the widget.
|
|
#[makepad_test]
|
|
#[ignore = "needs a Makepad headless backend; see REVIEWS/PDF_PARITY_PHASE1_STATUS.md"]
|
|
fn clicking_a_link_delivers_open_uri_to_the_host(app: TestApp) {
|
|
app.locator(Selector::id("pdf_page")).wait_visible();
|
|
|
|
click_pdf_point(&app, 150.0, 510.0);
|
|
|
|
app.locator(Selector::id("action_label"))
|
|
.wait_text("OpenUri: https://example.org/");
|
|
}
|
|
|
|
/// Clicking empty space must not emit anything, so the link assertion above
|
|
/// is not merely "any click produces an action".
|
|
#[makepad_test]
|
|
#[ignore = "needs a Makepad headless backend; see REVIEWS/PDF_PARITY_PHASE1_STATUS.md"]
|
|
fn clicking_empty_space_emits_nothing(app: TestApp) {
|
|
app.locator(Selector::id("pdf_page")).wait_visible();
|
|
|
|
// Far from every annotation and widget on the fixture page.
|
|
click_pdf_point(&app, 500.0, 300.0);
|
|
|
|
app.locator(Selector::id("action_label"))
|
|
.assert_text("no action");
|
|
}
|
|
|
|
/// Typing into a focused field must change the value through the real
|
|
/// keyboard path, not a synthesised `KeyInput`.
|
|
#[makepad_test]
|
|
#[ignore = "needs a Makepad headless backend; see REVIEWS/PDF_PARITY_PHASE1_STATUS.md"]
|
|
fn typing_into_a_form_field_reaches_the_widget(app: TestApp) {
|
|
app.locator(Selector::id("pdf_page")).wait_visible();
|
|
|
|
// The fixture's text field spans PDF 100,600 to 300,620 on page 1.
|
|
click_pdf_point(&app, 200.0, 610.0);
|
|
app.type_text("Z");
|
|
|
|
// Committing emits FieldChanged, which the host mirrors into the label.
|
|
app.press_return();
|
|
app.locator(Selector::id("action_label"))
|
|
.wait_text("FieldChanged");
|
|
}
|
|
|
|
/// A checkbox toggles on click, which is a different code path from the
|
|
/// buffered text-field edit.
|
|
#[makepad_test]
|
|
#[ignore = "needs a Makepad headless backend; see REVIEWS/PDF_PARITY_PHASE1_STATUS.md"]
|
|
fn clicking_the_checkbox_emits_a_field_change(app: TestApp) {
|
|
app.locator(Selector::id("pdf_page")).wait_visible();
|
|
|
|
// The fixture's checkbox is PDF 100,560 to 114,574.
|
|
click_pdf_point(&app, 107.0, 567.0);
|
|
|
|
app.locator(Selector::id("action_label"))
|
|
.wait_text("FieldChanged");
|
|
}
|
|
|
|
#[allow(unused)]
|
|
const _: () = {
|
|
// The window width is referenced only for documentation of the layout
|
|
// the coordinates assume; keep it compiling.
|
|
let _ = WINDOW_WIDTH;
|
|
};
|