Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
nigig-map / test (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
Remove direct dependencies on makepad-test and use the re-exported version from makepad-widgets instead. This avoids path dependency issues and follows the correct pattern for using Makepad crates. Changes: - Add 'test' feature to makepad-widgets dependencies - Remove direct makepad-test dependencies - Update imports to use makepad_widgets::makepad_test Affected crates: - crates/apps/map - crates/apps/pdf/pdf-makepad - crates/apps/spreadsheet/spreadsheet-ui
175 lines
6.5 KiB
Rust
175 lines
6.5 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 Studio
|
|
//! protocol against the test host in `src/bin/nigig-pdf-makepad.rs`, so the
|
|
//! whole path — event delivery, hit testing, action emission — is exercised.
|
|
//!
|
|
//! Headless by default. `MAKEPAD_TEST_VISIBLE=1` runs against a visible
|
|
//! Studio for debugging.
|
|
//!
|
|
//! 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.
|
|
//!
|
|
//! # Why these are `#[ignore]`
|
|
//!
|
|
//! They are ignored by default because the Studio hub cannot start an app in
|
|
//! this sandbox: the harness launches the binary with `--stdin-loop`, which
|
|
//! Makepad refuses without a Studio websocket, and the build exits 101
|
|
//! before startup. This is not specific to the PDF widget — upstream's own
|
|
//! `spreadsheet-ui` and `map` UI suites fail identically here with the same
|
|
//! error, so it is an environment limitation rather than a defect in this
|
|
//! code.
|
|
//!
|
|
//! They are checked in, and compiled by `cargo test`, so they cannot rot.
|
|
//! Run them where a Studio hub is available:
|
|
//!
|
|
//! ```bash
|
|
//! cargo test -p nigig-pdf-makepad --test ui -- --ignored --test-threads=1
|
|
//! ```
|
|
//!
|
|
//! 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
|
|
//! Studio 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.
|
|
const LABEL_HEIGHT: f64 = 24.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 Studio hub; see the module docs"]
|
|
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 Studio hub; see the module docs"]
|
|
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 Studio hub; see the module docs"]
|
|
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 Studio hub; see the module docs"]
|
|
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 Studio hub; see the module docs"]
|
|
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 Studio hub; see the module docs"]
|
|
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;
|
|
};
|