masonry_testing/
lib.rs

1// Copyright 2020 the Xilem Authors and the Druid Authors
2// SPDX-License-Identifier: Apache-2.0
3
4// After you edit the crate's doc comment, run this command, then check README.md for any missing links
5// cargo rdme --workspace-project=masonry_testing
6
7//! Headless runner for testing [Masonry](https://docs.rs/masonry/latest/) applications.
8//!
9//! The primary type from this crate is [`TestHarness`], which creates a host for any [Widget].
10//! The widget can of course have children, which allows this crate to be used for testing entire applications.
11//!
12//! The testing harness can:
13//!
14//! - Simulate any external event which Masonry handles, including mouse movement, key presses, text input, accessibility events.
15//! - Control the flow of time to the application (i.e. for testing animations).
16//! - Take screenshots of the application, save these to a file, and ensure that these are up-to-date.
17//!   See [Screenshots](#screenshots) for more details.
18//!
19//! <!-- Masonry itself depends on Masonry Testing, so we can't use an intra-doc link here. -->
20//! Testing in Masonry is also documented in the [Testing widgets in Masonry](https://docs.rs/masonry/latest/masonry/doc/doc_04_testing_widget/index.html)
21//! chapter in Masonry's book.
22//!
23//! This crate can be accessed for applications using Masonry as `masonry::testing`, if Masonry's `testing` feature is enabled.
24//! For applications which are using only [Masonry Core](masonry_core), you should depend on `masonry_testing` directly.
25//!
26//! # Screenshots
27//!
28//! Tests using `TestHarness` can include snapshot steps by using the [`assert_render_snapshot`] screenshot.
29//! This renders the application being tested, then compares it against the png file with the given name
30//! from the `screenshots` folder (in the package being tested, i.e. adjacent to its `Cargo.toml` file).
31//!
32//! Masonry Testing will update the reference file when the `MASONRY_TEST_BLESS` environment variable has a value of `1`.
33//! This can be used if the file doesn't exist, or there's an expected difference.
34//! The screenshots are losslessly compressed (using [oxipng]) and limited to a small maximum file size (this
35//! limit has an escape hatch).
36//! This ensures that the screenshots are small enough to embed in a git repository with limited risk
37//! of clone times growing unreasonably.
38//! UI screenshots compress well, so we expect this to be scalable.
39//!
40//! For repositories hosted on GitHub, this scheme also allows for including screenshots of your app or
41//! widgets in hosted documentation, although we haven't documented this publicly yet.
42//!
43//! # Examples
44//!
45//! For examples of this crate in use
46//!
47//! - To test applications: see the tests in Masonry's examples.
48//! - To test widgets: see the `tests` module in each widget in Masonry.
49
50// TODO: Remove any items listed as "Deferred"
51#![expect(missing_debug_implementations, reason = "Deferred: Noisy")]
52
53mod assert_any;
54mod assert_debug_panics;
55mod debug_name;
56mod harness;
57mod modular_widget;
58mod recorder_widget;
59mod screenshots;
60mod wrapper_widget;
61
62pub use assert_any::{assert_all, assert_any, assert_none};
63pub use assert_debug_panics::assert_debug_panics_inner;
64pub use debug_name::DebugName;
65pub use harness::{PRIMARY_MOUSE, TestHarness, TestHarnessParams};
66pub use modular_widget::ModularWidget;
67pub use recorder_widget::{Record, Recorder, Recording};
68pub use wrapper_widget::WrapperWidget;
69
70use masonry_core::core::{Widget, WidgetId};
71
72/// External trait implemented for all widgets.
73///
74/// Implements helper methods useful for unit testing.
75pub trait TestWidgetExt: Widget + Sized + 'static {
76    /// Wrap this widget in a [`Recorder`] that records all method calls.
77    fn record(self) -> Recorder<Self> {
78        let recording = Recording::default();
79        Recorder::new(self, &recording)
80    }
81}
82
83impl<W: Widget + 'static> TestWidgetExt for W {}
84
85// TODO - We eventually want to remove the ability to reserve widget ids.
86// See https://github.com/linebender/xilem/issues/1255
87/// Convenience function to return an array of unique widget ids.
88pub fn widget_ids<const N: usize>() -> [WidgetId; N] {
89    std::array::from_fn(|_| WidgetId::next())
90}