presentar_test/
lib.rs

1#![allow(clippy::missing_const_for_fn)]
2#![allow(clippy::match_same_arms)]
3#![allow(clippy::unwrap_used)]
4#![allow(clippy::disallowed_methods)]
5#![allow(clippy::too_many_lines)]
6#![allow(clippy::struct_excessive_bools)]
7#![allow(clippy::needless_pass_by_value)]
8#![allow(clippy::doc_markdown)]
9#![allow(clippy::cast_possible_wrap)]
10#![allow(clippy::cast_sign_loss)]
11#![allow(clippy::cast_precision_loss)]
12#![allow(clippy::should_implement_trait)]
13#![allow(clippy::similar_names)]
14#![allow(clippy::derive_partial_eq_without_eq)]
15#![allow(clippy::map_unwrap_or)]
16#![allow(clippy::module_name_repetitions)]
17#![allow(clippy::manual_let_else)]
18#![allow(clippy::if_not_else)]
19#![allow(clippy::uninlined_format_args)]
20#![allow(clippy::type_complexity)]
21#![allow(clippy::many_single_char_names)]
22#![allow(clippy::fn_params_excessive_bools)]
23#![allow(clippy::too_many_arguments)]
24#![allow(clippy::manual_assert)]
25#![allow(clippy::suboptimal_flops)]
26#![allow(clippy::float_cmp)]
27#![allow(clippy::clone_on_copy)]
28#![allow(clippy::cloned_instead_of_copied)]
29#![allow(clippy::single_match)]
30#![allow(clippy::unnecessary_wraps)]
31#![allow(clippy::missing_const_for_fn)]
32#![allow(clippy::struct_field_names)]
33#![allow(clippy::unused_self)]
34#![allow(clippy::trivially_copy_pass_by_ref)]
35#![allow(clippy::match_like_matches_macro)]
36#![allow(clippy::let_and_return)]
37#![allow(clippy::explicit_iter_loop)]
38#![allow(clippy::ptr_arg)]
39#![allow(clippy::use_self)]
40#![allow(clippy::cast_lossless)]
41#![allow(clippy::items_after_statements)]
42#![allow(clippy::default_trait_access)]
43#![allow(clippy::redundant_closure_for_method_calls)]
44#![allow(clippy::iter_without_into_iter)]
45#![allow(clippy::if_then_some_else_none)]
46#![allow(clippy::semicolon_if_nothing_returned)]
47#![allow(clippy::option_if_let_else)]
48#![allow(clippy::case_sensitive_file_extension_comparisons)]
49#![allow(clippy::format_push_string)]
50#![allow(clippy::same_item_push)]
51#![allow(clippy::naive_bytecount)]
52#![allow(clippy::unnecessary_to_owned)]
53#![allow(clippy::must_use_candidate)]
54#![allow(clippy::return_self_not_must_use)]
55#![allow(clippy::missing_panics_doc)]
56#![allow(clippy::missing_errors_doc)]
57#![allow(clippy::collapsible_if)]
58#![allow(clippy::test_attr_in_doctest)]
59#![allow(clippy::manual_div_ceil)]
60#![allow(clippy::new_without_default)]
61#![allow(clippy::comparison_to_empty)]
62#![allow(clippy::get_first)]
63#![allow(clippy::unreadable_literal)]
64#![allow(clippy::duplicated_attributes)]
65//! Testing harness for Presentar applications.
66//!
67//! Zero external dependencies. Pure Rust + WASM only.
68//!
69//! # Proc Macros
70//!
71//! Use `#[presentar_test]` for widget and integration tests:
72//!
73//! ```ignore
74//! use presentar_test::presentar_test;
75//!
76//! #[presentar_test]
77//! fn test_button() {
78//!     let harness = Harness::new(Button::new("Click"));
79//!     harness.assert_exists("Button");
80//! }
81//!
82//! #[presentar_test(timeout = 5000, fixture = "app.tar")]
83//! fn test_with_fixture() {
84//!     // Fixture is loaded automatically
85//! }
86//! ```
87
88mod a11y;
89pub mod bdd;
90pub mod build;
91pub mod fixture;
92pub mod grade;
93mod harness;
94mod selector;
95mod snapshot;
96pub mod tui;
97
98pub use a11y::{
99    aria_from_widget, A11yChecker, A11yConfig, A11yReport, A11yViolation, AriaAttributes,
100    AriaChecked, AriaLive, AutocompleteValue, FormA11yChecker, FormA11yReport, FormA11yRule,
101    FormAccessibility, FormFieldA11y, FormFieldGroup, FormViolation, Impact, InputType,
102    MIN_FOCUS_INDICATOR_AREA, MIN_TOUCH_TARGET_SIZE,
103};
104pub use bdd::{describe, describe_and_assert, expect, Expectation, TestContext};
105pub use build::{
106    BuildInfo, BuildMode, BundleAnalysis, BundleAnalyzer, BundleError, SizeRecord, SizeTracker,
107    WasmSection,
108};
109pub use fixture::{
110    Fixture, FixtureBuilder, FixtureContext, FixtureError, FixtureManifest, TestData,
111};
112pub use grade::{
113    AccessibilityGates, AppQualityScore, Criterion, DataGates, DocumentationGates,
114    EvaluationBuilder, GateCheckResult, GateViolation, Grade, PerformanceGates, QualityGates,
115    QualityScoreBuilder, ReportCard, ScoreBreakdown, ViolationSeverity,
116};
117pub use harness::Harness;
118pub use selector::{Selector, SelectorParser};
119pub use snapshot::{ComparisonResult, Image, Snapshot};
120
121// TUI Testing Framework (SPEC-024 Section 12 & 13)
122// Tests DEFINE interface - implementation follows
123pub use tui::{
124    expect_frame, AsyncUpdateAssertion, BenchmarkHarness, BenchmarkResult, DiffEntry,
125    FrameAssertion, PerformanceTargets, RenderMetrics, SnapshotDiff, SnapshotError, TuiCell,
126    TuiSnapshot, TuiTestBackend,
127};
128
129// Re-export proc macros for convenient access
130pub use presentar_test_macros::{assert_snapshot, describe_suite, fixture, presentar_test};
131
132// =============================================================================
133// COMPUTEBLOCK ARCHITECTURAL ENFORCEMENT (SPEC-024)
134// =============================================================================
135//
136// TESTS DEFINE INTERFACE. IMPLEMENTATION FOLLOWS.
137//
138// These macros make it IMPOSSIBLE to build presentar applications without tests.
139// The test creates a "proof" type that the implementation requires.
140// Without the test -> no proof type -> compile error.
141//
142// Usage:
143//   1. In tests: #[interface_test(MyFeature)] fn test_xxx() { ... }
144//   2. In impl:  #[requires_interface(MyFeature)] impl MyFeature { ... }
145//
146// If you try to use #[requires_interface] without the corresponding
147// #[interface_test], the build FAILS. This is architectural, not advisory.
148
149pub use presentar_test_macros::{computeblock, interface_test, requires_interface};