Skip to main content

jugar_probar/
lib.rs

1//! Probar: Rust-Native Testing Framework for WASM Games
2//!
3//! Per spec Section 6.1: Probar (Spanish: "to test/prove") is a pure Rust
4//! alternative to Playwright/Puppeteer, designed for WASM game testing.
5//!
6//! # Architecture
7//!
8//! ```text
9//! ┌─────────────────────────────────────────────────────────────────┐
10//! │                    PROBAR Architecture                           │
11//! ├─────────────────────────────────────────────────────────────────┤
12//! │   ┌────────────┐    ┌────────────┐    ┌────────────┐            │
13//! │   │ Test Spec  │    │ WASM       │    │ Headless   │            │
14//! │   │ (Rust)     │───►│ Test       │───►│ Browser    │            │
15//! │   │            │    │ Harness    │    │ (chromium) │            │
16//! │   └────────────┘    └────────────┘    └────────────┘            │
17//! └─────────────────────────────────────────────────────────────────┘
18//! ```
19
20#![allow(missing_docs)]
21// probar IS test tooling — unwrap/expect throughout is acceptable test infrastructure
22#![allow(clippy::disallowed_methods)]
23// Lints are configured in workspace Cargo.toml [workspace.lints.clippy]
24// Allow large stack arrays/frames in tests (e.g., test data generation)
25#![cfg_attr(test, allow(clippy::large_stack_arrays, clippy::large_stack_frames))]
26
27// Contract assertions from YAML (pv codegen)
28#[macro_use]
29#[allow(unused_macros, clippy::duplicated_attributes)]
30mod generated_contracts;
31
32/// Brick Architecture: Tests ARE the Interface (PROBAR-SPEC-009)
33///
34/// Core abstraction where UI components are defined by test assertions.
35#[allow(
36    clippy::missing_errors_doc,
37    clippy::must_use_candidate,
38    clippy::missing_const_for_fn,
39    clippy::doc_markdown
40)]
41pub mod brick;
42
43/// BrickHouse: Budgeted Composition of Bricks (PROBAR-SPEC-009)
44///
45/// Compose multiple bricks with a total performance budget.
46#[allow(
47    clippy::missing_errors_doc,
48    clippy::must_use_candidate,
49    clippy::missing_const_for_fn,
50    clippy::doc_markdown,
51    clippy::expect_used
52)]
53pub mod brick_house;
54
55#[allow(
56    clippy::suboptimal_flops,
57    clippy::cast_precision_loss,
58    clippy::struct_excessive_bools,
59    clippy::missing_errors_doc,
60    clippy::must_use_candidate,
61    clippy::missing_const_for_fn,
62    clippy::unnecessary_wraps,
63    clippy::doc_markdown
64)]
65mod accessibility;
66mod assertion;
67#[allow(
68    clippy::missing_errors_doc,
69    clippy::must_use_candidate,
70    clippy::missing_const_for_fn,
71    clippy::doc_markdown
72)]
73mod bridge;
74mod browser;
75/// Real CDP-backed driver. Before this existed, `ProbarDriver` had exactly
76/// one implementation -- `MockDriver` -- so every layer built on the trait
77/// drove a mock (issue #2473).
78#[cfg(feature = "browser")]
79mod chromium_driver;
80#[allow(
81    clippy::missing_errors_doc,
82    clippy::must_use_candidate,
83    clippy::missing_const_for_fn,
84    clippy::doc_markdown,
85    dead_code
86)]
87mod driver;
88mod event;
89mod fuzzer;
90mod harness;
91#[allow(
92    clippy::missing_errors_doc,
93    clippy::must_use_candidate,
94    clippy::missing_const_for_fn,
95    clippy::unnecessary_wraps,
96    clippy::doc_markdown
97)]
98mod locator;
99#[allow(
100    clippy::missing_errors_doc,
101    clippy::must_use_candidate,
102    clippy::missing_const_for_fn,
103    clippy::doc_markdown,
104    clippy::cast_precision_loss,
105    clippy::format_push_string,
106    clippy::needless_raw_string_hashes
107)]
108mod reporter;
109mod result;
110#[allow(
111    clippy::missing_errors_doc,
112    clippy::must_use_candidate,
113    clippy::missing_const_for_fn,
114    clippy::unnecessary_wraps,
115    clippy::doc_markdown,
116    clippy::if_not_else,
117    clippy::ptr_as_ptr,
118    clippy::expect_used,
119    unsafe_code
120)]
121mod runtime;
122mod simulation;
123mod snapshot;
124#[cfg(feature = "media")]
125mod visual_regression;
126
127/// State Synchronization Linting (PROBAR-SPEC-WASM-001)
128///
129/// Static analysis for detecting WASM closure state sync anti-patterns.
130#[allow(
131    clippy::missing_errors_doc,
132    clippy::must_use_candidate,
133    clippy::missing_const_for_fn,
134    clippy::doc_markdown
135)]
136pub mod lint;
137
138/// Mock Runtime for WASM Callback Testing (PROBAR-SPEC-WASM-001)
139///
140/// Test WASM callback patterns without browser APIs.
141#[allow(
142    clippy::missing_errors_doc,
143    clippy::must_use_candidate,
144    clippy::missing_const_for_fn,
145    clippy::doc_markdown
146)]
147pub mod mock;
148
149/// Compliance Checking for WASM Threading (PROBAR-SPEC-WASM-001)
150///
151/// Verify projects follow WASM threading best practices.
152#[allow(
153    clippy::missing_errors_doc,
154    clippy::must_use_candidate,
155    clippy::missing_const_for_fn,
156    clippy::doc_markdown
157)]
158pub mod comply;
159
160/// Page Object Model Support (Feature 19)
161#[allow(
162    clippy::missing_errors_doc,
163    clippy::must_use_candidate,
164    clippy::missing_const_for_fn,
165    clippy::doc_markdown
166)]
167mod page_object;
168
169/// Fixture Management (Feature 20)
170#[allow(
171    clippy::missing_errors_doc,
172    clippy::must_use_candidate,
173    clippy::missing_const_for_fn,
174    clippy::doc_markdown
175)]
176mod fixture;
177
178/// TUI Testing Support (Feature 21 - EDD Compliance)
179#[cfg(feature = "tui")]
180#[allow(
181    clippy::missing_errors_doc,
182    clippy::must_use_candidate,
183    clippy::missing_const_for_fn,
184    clippy::doc_markdown
185)]
186pub mod tui;
187
188/// TUI Load Testing (Framework-Agnostic Performance Testing)
189///
190/// Test TUI performance with large datasets, hang detection, and frame timing.
191/// Works with any TUI framework (presentar-terminal, crossterm, etc.).
192#[allow(
193    clippy::missing_errors_doc,
194    clippy::must_use_candidate,
195    clippy::missing_const_for_fn,
196    clippy::doc_markdown
197)]
198pub mod tui_load;
199
200/// Deterministic Replay System (Feature 23 - EDD Compliance)
201#[allow(
202    clippy::missing_errors_doc,
203    clippy::must_use_candidate,
204    clippy::missing_const_for_fn,
205    clippy::doc_markdown
206)]
207pub mod replay;
208
209/// UX Coverage Metrics (Feature 24 - EDD Compliance)
210#[allow(
211    clippy::missing_errors_doc,
212    clippy::must_use_candidate,
213    clippy::missing_const_for_fn,
214    clippy::doc_markdown
215)]
216pub mod ux_coverage;
217
218/// Device Emulation and Geolocation Mocking (Features 15-16)
219#[allow(
220    clippy::missing_errors_doc,
221    clippy::must_use_candidate,
222    clippy::missing_const_for_fn,
223    clippy::doc_markdown
224)]
225pub mod emulation;
226
227/// Media Generation Module (Spec: missing-features-in-pure-rust.md)
228#[cfg(feature = "media")]
229#[allow(
230    clippy::missing_errors_doc,
231    clippy::must_use_candidate,
232    clippy::missing_const_for_fn,
233    clippy::doc_markdown,
234    clippy::cast_possible_truncation
235)]
236pub mod media;
237
238/// Watch Mode with Hot Reload (Feature 6)
239/// Note: Not available on WASM targets (requires filesystem access)
240#[cfg(all(not(target_arch = "wasm32"), feature = "watch"))]
241#[allow(
242    clippy::missing_errors_doc,
243    clippy::must_use_candidate,
244    clippy::missing_const_for_fn,
245    clippy::doc_markdown
246)]
247pub mod watch;
248
249/// Execution Tracing (Feature 9)
250#[allow(
251    clippy::missing_errors_doc,
252    clippy::must_use_candidate,
253    clippy::missing_const_for_fn,
254    clippy::doc_markdown,
255    clippy::cast_possible_truncation
256)]
257pub mod tracing_support;
258
259/// Network Request Interception (Feature 7)
260#[allow(
261    clippy::missing_errors_doc,
262    clippy::must_use_candidate,
263    clippy::missing_const_for_fn,
264    clippy::doc_markdown
265)]
266pub mod network;
267
268/// Wait Mechanisms (PMAT-005)
269#[allow(
270    clippy::missing_errors_doc,
271    clippy::must_use_candidate,
272    clippy::missing_const_for_fn,
273    clippy::doc_markdown
274)]
275pub mod wait;
276
277/// WebSocket Monitoring (Feature 8)
278#[allow(
279    clippy::missing_errors_doc,
280    clippy::must_use_candidate,
281    clippy::missing_const_for_fn,
282    clippy::doc_markdown
283)]
284pub mod websocket;
285
286/// Performance Profiling (Feature 10)
287#[allow(
288    clippy::missing_errors_doc,
289    clippy::must_use_candidate,
290    clippy::missing_const_for_fn,
291    clippy::doc_markdown,
292    clippy::cast_possible_truncation
293)]
294pub mod performance;
295
296/// Multi-Browser Context Management (Feature 14)
297#[allow(
298    clippy::missing_errors_doc,
299    clippy::must_use_candidate,
300    clippy::missing_const_for_fn,
301    clippy::doc_markdown
302)]
303pub mod context;
304
305/// WASM Coverage Tooling (spec: probar-wasm-coverage-tooling.md)
306#[allow(
307    clippy::module_name_repetitions,
308    clippy::must_use_candidate,
309    clippy::missing_const_for_fn,
310    clippy::missing_errors_doc,
311    clippy::doc_markdown,
312    clippy::cast_possible_truncation,
313    clippy::cast_precision_loss,
314    clippy::use_self,
315    clippy::inline_always,
316    clippy::similar_names,
317    clippy::missing_panics_doc,
318    clippy::suboptimal_flops,
319    clippy::uninlined_format_args,
320    clippy::redundant_closure_for_method_calls
321)]
322pub mod coverage;
323
324/// Zero-JavaScript Web Asset Generation (Advanced Feature E)
325#[allow(
326    clippy::missing_errors_doc,
327    clippy::must_use_candidate,
328    clippy::missing_const_for_fn,
329    clippy::doc_markdown
330)]
331pub mod web;
332
333/// Pixel-Level GUI Coverage Visualization (Advanced Feature A)
334///
335/// Requires the `media` feature for `image` crate support.
336#[cfg(feature = "media")]
337#[allow(
338    clippy::missing_errors_doc,
339    clippy::must_use_candidate,
340    clippy::missing_const_for_fn,
341    clippy::doc_markdown,
342    clippy::cast_precision_loss
343)]
344pub mod pixel_coverage;
345
346/// GPU Pixel Testing: Atomic verification of CUDA kernel correctness
347#[allow(
348    clippy::missing_errors_doc,
349    clippy::must_use_candidate,
350    clippy::missing_const_for_fn,
351    clippy::doc_markdown
352)]
353pub mod gpu_pixels;
354
355/// WASM Runner with Hot Reload (Advanced Feature D)
356#[allow(
357    clippy::missing_errors_doc,
358    clippy::must_use_candidate,
359    clippy::missing_const_for_fn,
360    clippy::doc_markdown
361)]
362pub mod runner;
363
364/// Performance Benchmarking with Renacer Integration (Advanced Feature C)
365#[allow(
366    clippy::missing_errors_doc,
367    clippy::must_use_candidate,
368    clippy::missing_const_for_fn,
369    clippy::doc_markdown,
370    clippy::cast_precision_loss
371)]
372pub mod perf;
373
374/// Renacer Integration for Deep WASM Tracing (Issue #9)
375#[allow(
376    clippy::missing_errors_doc,
377    clippy::must_use_candidate,
378    clippy::missing_const_for_fn,
379    clippy::doc_markdown
380)]
381pub mod renacer_integration;
382
383/// CDP Profiler-based Code Coverage (Issue #10)
384#[allow(
385    clippy::missing_errors_doc,
386    clippy::must_use_candidate,
387    clippy::missing_const_for_fn,
388    clippy::doc_markdown
389)]
390pub mod cdp_coverage;
391
392/// Test Sharding for Distributed Execution (Feature G.5)
393#[allow(
394    clippy::missing_errors_doc,
395    clippy::must_use_candidate,
396    clippy::missing_const_for_fn,
397    clippy::doc_markdown
398)]
399pub mod shard;
400
401/// Clock Manipulation for Deterministic Tests (Feature G.6)
402#[allow(
403    clippy::missing_errors_doc,
404    clippy::must_use_candidate,
405    clippy::missing_const_for_fn,
406    clippy::doc_markdown
407)]
408pub mod clock;
409
410/// WASM Thread Capabilities Detection (Advanced Testing Concepts)
411#[allow(
412    clippy::missing_errors_doc,
413    clippy::must_use_candidate,
414    clippy::missing_const_for_fn,
415    clippy::doc_markdown
416)]
417pub mod capabilities;
418
419/// WASM Strict Mode Enforcement (Advanced Testing Concepts)
420#[allow(
421    clippy::missing_errors_doc,
422    clippy::must_use_candidate,
423    clippy::missing_const_for_fn,
424    clippy::doc_markdown
425)]
426pub mod strict;
427
428/// Streaming UX Validators (Advanced Testing Concepts)
429#[allow(
430    clippy::missing_errors_doc,
431    clippy::must_use_candidate,
432    clippy::missing_const_for_fn,
433    clippy::doc_markdown
434)]
435pub mod validators;
436
437/// Zero-JavaScript Validation for WASM-First Applications (PROBAR-SPEC-012).
438///
439/// Validates that WASM applications contain NO user-generated JavaScript, CSS, or HTML.
440#[allow(
441    clippy::missing_errors_doc,
442    clippy::must_use_candidate,
443    clippy::missing_const_for_fn,
444    clippy::doc_markdown,
445    clippy::too_long_first_doc_paragraph
446)]
447pub mod zero_js;
448
449/// WASM Worker Test Harness (PROBAR-SPEC-013).
450///
451/// Comprehensive testing framework for Web Workers in WASM applications.
452#[allow(
453    clippy::missing_errors_doc,
454    clippy::must_use_candidate,
455    clippy::missing_const_for_fn,
456    clippy::doc_markdown,
457    clippy::too_long_first_doc_paragraph
458)]
459pub mod worker_harness;
460
461/// Docker-based Cross-Browser WASM Testing (PROBAR-SPEC-014).
462///
463/// Enables cross-browser testing via Docker containers with COOP/COEP support.
464#[cfg(feature = "docker")]
465#[allow(
466    clippy::missing_errors_doc,
467    clippy::must_use_candidate,
468    clippy::missing_const_for_fn,
469    clippy::doc_markdown,
470    clippy::too_long_first_doc_paragraph
471)]
472pub mod docker;
473
474/// Dialog Handling for E2E Testing (Feature G.8)
475#[allow(
476    clippy::missing_errors_doc,
477    clippy::must_use_candidate,
478    clippy::missing_const_for_fn,
479    clippy::doc_markdown
480)]
481pub mod dialog;
482
483/// File Upload/Download Operations (Feature G.8)
484#[allow(
485    clippy::missing_errors_doc,
486    clippy::must_use_candidate,
487    clippy::missing_const_for_fn,
488    clippy::doc_markdown
489)]
490pub mod file_ops;
491
492/// HAR Recording Module (Spec: G.2 Network Interception)
493#[allow(
494    clippy::missing_docs_in_private_items,
495    clippy::missing_errors_doc,
496    clippy::must_use_candidate,
497    clippy::missing_const_for_fn,
498    clippy::doc_markdown
499)]
500pub mod har;
501
502/// Playbook Testing: State Machine Verification (PROBAR-004)
503/// YAML-driven state machine testing with M1-M5 mutation classes.
504#[allow(
505    clippy::missing_errors_doc,
506    clippy::must_use_candidate,
507    clippy::missing_const_for_fn,
508    clippy::doc_markdown,
509    clippy::expect_used,
510    clippy::many_single_char_names,
511    clippy::suspicious_operation_groupings,
512    missing_docs,
513    missing_debug_implementations
514)]
515pub mod playbook;
516
517/// AV Sync Testing: Verify rendered audio-visual synchronization against EDL ground truth.
518#[allow(
519    clippy::missing_errors_doc,
520    clippy::must_use_candidate,
521    clippy::missing_const_for_fn,
522    clippy::doc_markdown
523)]
524pub mod av_sync;
525
526/// Audio Quality Verification: levels, clipping, silence analysis.
527#[allow(
528    clippy::missing_errors_doc,
529    clippy::must_use_candidate,
530    clippy::missing_const_for_fn,
531    clippy::doc_markdown,
532    clippy::cast_precision_loss
533)]
534pub mod audio_quality;
535
536/// Video Quality Verification: codec, resolution, FPS, duration validation.
537#[allow(
538    clippy::missing_errors_doc,
539    clippy::must_use_candidate,
540    clippy::missing_const_for_fn,
541    clippy::doc_markdown,
542    clippy::cast_precision_loss
543)]
544pub mod video_quality;
545
546/// Animation Verification: timing, easing curves, physics events.
547#[allow(
548    clippy::missing_errors_doc,
549    clippy::must_use_candidate,
550    clippy::missing_const_for_fn,
551    clippy::doc_markdown
552)]
553pub mod animation;
554
555/// Presentar YAML Support (PROBAR-SPEC-015)
556///
557/// Native support for testing presentar TUI configurations with
558/// 100-point falsification protocol (F001-F100).
559#[allow(
560    clippy::missing_errors_doc,
561    clippy::must_use_candidate,
562    clippy::missing_const_for_fn,
563    clippy::doc_markdown
564)]
565pub mod presentar;
566
567/// LLM Testing: Correctness assertions and load testing for OpenAI-compatible APIs.
568///
569/// Feature-gated behind `llm`. Provides HTTP client, assertion builders,
570/// concurrent load testing, and Markdown/JSON reporting.
571#[cfg(any(feature = "llm-types", feature = "llm"))]
572#[allow(
573    clippy::missing_errors_doc,
574    clippy::must_use_candidate,
575    clippy::missing_const_for_fn,
576    clippy::doc_markdown
577)]
578pub mod llm;
579
580pub use accessibility::{
581    AccessibilityAudit, AccessibilityConfig, AccessibilityIssue, AccessibilityValidator, Color,
582    ContrastAnalysis, ContrastPair, FlashDetector, FlashResult, FocusConfig, KeyboardIssue,
583    Severity, MIN_CONTRAST_LARGE, MIN_CONTRAST_NORMAL, MIN_CONTRAST_UI,
584};
585pub use animation::{
586    sample_easing, verify_easing, verify_events, verify_timeline, AnimationEvent,
587    AnimationEventType, AnimationReport, AnimationTimeline, AnimationVerdict, EasingFunction,
588    EasingVerification, EventResult, Keyframe, ObservedEvent,
589};
590pub use assertion::{
591    retry_contains, retry_eq, retry_none, retry_some, retry_true, Assertion, AssertionCheckResult,
592    AssertionFailure, AssertionMode, AssertionResult, AssertionSummary, EnergyVerifier,
593    EquationContext, EquationResult, EquationVerifier, InvariantVerifier, KinematicVerifier,
594    MomentumVerifier, RetryAssertion, RetryConfig, RetryError, RetryResult, SoftAssertionError,
595    SoftAssertions, Variable,
596};
597pub use audio_quality::{
598    analyze_audio, analyze_samples, detect_clipping, detect_silence, AudioLevels,
599    AudioQualityConfig, AudioQualityReport, AudioVerdict, ClippingReport, SilenceRegion,
600    SilenceReport,
601};
602pub use av_sync::{
603    compare_edl_to_onsets, default_edl_path, detect_onsets, extract_audio, AudioOnset,
604    AudioTickPlacement, AvSyncReport, DetectionConfig, EditDecision, EditDecisionList,
605    SegmentSyncResult, SyncVerdict, TickDelta, DEFAULT_SAMPLE_RATE,
606};
607pub use bridge::{
608    BridgeConnection, DiffRegion, EntitySnapshot, GameStateData, GameStateSnapshot, SnapshotCache,
609    StateBridge, VisualDiff,
610};
611pub use browser::{Browser, BrowserConfig, BrowserConsoleLevel, BrowserConsoleMessage, Page};
612pub use capabilities::{
613    CapabilityError, CapabilityStatus, RequiredHeaders, WasmThreadCapabilities, WorkerEmulator,
614    WorkerMessage, WorkerState,
615};
616pub use cdp_coverage::{
617    CoverageConfig, CoverageRange, CoverageReport, CoveredFunction, FunctionCoverage, JsCoverage,
618    LineCoverage, ScriptCoverage, SourceMapEntry, WasmCoverage, WasmSourceMap,
619};
620#[cfg(feature = "browser")]
621pub use chromium_driver::ChromiumDriver;
622pub use clock::{
623    create_clock, Clock, ClockController, ClockError, ClockOptions, ClockState, FakeClock,
624};
625pub use context::{
626    BrowserContext, ContextConfig, ContextManager, ContextPool, ContextPoolStats, ContextState,
627    Cookie, Geolocation, SameSite, StorageState,
628};
629pub use dialog::{
630    AutoDialogBehavior, Dialog, DialogAction, DialogExpectation, DialogHandler,
631    DialogHandlerBuilder, DialogType,
632};
633#[cfg(feature = "browser")]
634pub use driver::{BrowserController, ProbarDriver};
635pub use driver::{
636    DeviceDescriptor, DriverConfig, ElementHandle, MockDriver, NetworkInterceptor, NetworkResponse,
637    PageMetrics, Screenshot,
638};
639pub use event::{InputEvent, Touch, TouchAction};
640pub use file_ops::{
641    guess_mime_type, Download, DownloadManager, DownloadState, FileChooser, FileInput,
642};
643pub use fixture::{
644    Fixture, FixtureBuilder, FixtureManager, FixtureScope, FixtureState, SimpleFixture,
645};
646pub use fuzzer::{
647    FuzzerConfig, InputFuzzer, InvariantCheck, InvariantChecker, InvariantViolation, Seed,
648};
649pub use har::{
650    Har, HarBrowser, HarCache, HarContent, HarCookie, HarCreator, HarEntry, HarError, HarHeader,
651    HarLog, HarOptions, HarPlayer, HarPostData, HarPostParam, HarQueryParam, HarRecorder,
652    HarRequest, HarResponse, HarTimings, NotFoundBehavior,
653};
654pub use harness::{TestCase, TestHarness, TestResult, TestSuite};
655pub use locator::{
656    expect, BoundingBox, DragBuilder, DragOperation, Expect, ExpectAssertion, Locator,
657    LocatorAction, LocatorOptions, LocatorQuery, Point, Selector, DEFAULT_POLL_INTERVAL_MS,
658    DEFAULT_TIMEOUT_MS,
659};
660pub use network::{
661    CapturedRequest, HttpMethod, MockResponse, NetworkInterception, NetworkInterceptionBuilder,
662    Route, UrlPattern,
663};
664pub use page_object::{
665    PageObject, PageObjectBuilder, PageObjectInfo, PageRegistry, SimplePageObject, UrlMatcher,
666};
667pub use performance::{
668    Measurement, MetricStats, MetricType, PerformanceMonitor, PerformanceProfile,
669    PerformanceProfiler, PerformanceProfilerBuilder, PerformanceSummary, PerformanceThreshold,
670};
671pub use playbook::{
672    calculate_mutation_score, check_complexity_violation, to_dot, Action as PlaybookAction,
673    ActionExecutor, Assertion as PlaybookAssertion, AssertionFailure as PlaybookAssertionFailure,
674    ComplexityAnalyzer, ComplexityClass, ComplexityResult, DeterminismInfo,
675    ExecutionResult as PlaybookExecutionResult, ExecutorError, Invariant, IssueSeverity,
676    MutantResult, MutationClass, MutationGenerator, MutationScore, PerformanceBudget, Playbook,
677    PlaybookError, PlaybookExecutor, ReachabilityInfo, State as PlaybookState, StateMachine,
678    StateMachineValidator, Transition as PlaybookTransition, ValidationIssue, ValidationResult,
679    WaitCondition as PlaybookWaitCondition,
680};
681pub use presentar::{
682    generate_falsification_playbook, parse_and_validate as parse_and_validate_presentar,
683    validate_config as validate_presentar_config, Cell as PresentarCell, Color as PresentarColor,
684    FalsificationCheck, FalsificationResult, KeybindingConfig, LayoutConfig, PanelConfig,
685    PanelConfigs, PanelType, PresentarConfig, PresentarError, TerminalAssertion, TerminalSnapshot,
686    ThemeConfig, ValidationResult as PresentarValidationResult, FALSIFICATION_COUNT,
687    SCHEMA_VERSION,
688};
689pub use renacer_integration::{
690    ChromeTrace, ChromeTraceEvent, TraceCollector, TraceContext, TraceSpan,
691    TracingConfig as RenacerTracingConfig,
692};
693pub use replay::{
694    Replay, ReplayHeader, ReplayPlayer, ReplayRecorder, StateCheckpoint, TimedInput,
695    VerificationResult, REPLAY_FORMAT_VERSION,
696};
697pub use reporter::{
698    AndonCordPulled, FailureMode, Reporter, TestResultEntry, TestStatus, TraceData,
699};
700pub use result::{ProbarError, ProbarResult};
701pub use runtime::{
702    ComponentId, EntityId, FrameResult, GameHostState, MemoryView, ProbarComponent, ProbarEntity,
703    RuntimeConfig, StateDelta, WasmRuntime,
704};
705pub use shard::{ShardConfig, ShardParseError, ShardReport, ShardedRunner};
706pub use simulation::{
707    run_replay, run_simulation, RandomWalkAgent, RecordedFrame, ReplayResult, SimulatedGameState,
708    SimulationConfig, SimulationRecording,
709};
710pub use snapshot::{Snapshot, SnapshotConfig, SnapshotDiff};
711pub use strict::{
712    ChecklistError, ConsoleCapture, ConsoleSeverity, ConsoleValidationError, E2ETestChecklist,
713    WasmStrictMode,
714};
715pub use tracing_support::{
716    ConsoleLevel, ConsoleMessage, EventCategory, EventLevel, ExecutionTracer, NetworkEvent,
717    SpanStatus, TraceArchive, TraceMetadata, TracedEvent, TracedSpan, TracingConfig,
718};
719#[cfg(feature = "tui")]
720pub use tui::{
721    expect_frame, FrameAssertion, FrameSequence, MultiValueTracker, SnapshotManager, TuiFrame,
722    TuiSnapshot, TuiTestBackend, ValueTracker,
723};
724pub use tui_load::{
725    ComponentTimings, DataGenerator, IntegrationLoadTest, SyntheticItem, TuiFrameMetrics,
726    TuiLoadAssertion, TuiLoadConfig, TuiLoadError, TuiLoadResult, TuiLoadTest,
727};
728pub use ux_coverage::{
729    calculator_coverage, game_coverage, ElementCoverage, ElementId, InteractionType, StateId,
730    TrackedInteraction, UxCoverageBuilder, UxCoverageReport, UxCoverageTracker,
731};
732pub use validators::{
733    CompressionAlgorithm, PartialResult, ScreenshotContent, StateTransition, StreamingMetric,
734    StreamingMetricRecord, StreamingState, StreamingUxValidator, StreamingValidationError,
735    StreamingValidationResult, TestExecutionStats, VuMeterConfig, VuMeterError, VuMeterSample,
736};
737pub use video_quality::{
738    build_ffprobe_args, parse_ffprobe_json, probe_video, validate_video, VideoCheck,
739    VideoExpectations, VideoProbe, VideoQualityReport, VideoVerdict,
740};
741#[cfg(feature = "media")]
742pub use visual_regression::{
743    perceptual_diff, ImageDiffResult, MaskRegion, ScreenshotComparison, VisualRegressionConfig,
744    VisualRegressionTester,
745};
746pub use wait::{
747    wait_timeout, wait_until, FnCondition, LoadState, NavigationOptions, PageEvent, WaitCondition,
748    WaitOptions, WaitResult, Waiter, DEFAULT_WAIT_TIMEOUT_MS, NETWORK_IDLE_THRESHOLD_MS,
749};
750#[cfg(all(not(target_arch = "wasm32"), feature = "watch"))]
751pub use watch::{
752    FileChange, FileChangeKind, FileWatcher, FnWatchHandler, WatchBuilder, WatchConfig,
753    WatchHandler, WatchStats,
754};
755// Brick Architecture (PROBAR-SPEC-009)
756pub use brick::{
757    Brick, BrickAssertion, BrickBudget, BrickError, BrickPhase, BrickResult, BrickVerification,
758    BudgetViolation,
759};
760// Zero-Artifact Architecture (PROBAR-SPEC-009-P7)
761pub use brick::{
762    AudioBrick, AudioParam, BrickWorkerMessage, BrickWorkerMessageDirection, EventBinding,
763    EventBrick, EventHandler, EventType, FieldType, RingBufferConfig, WorkerBrick,
764    WorkerTransition,
765};
766pub use brick_house::{BrickHouse, BrickHouseBuilder, BrickTiming, BudgetReport, JidokaAlert};
767pub use websocket::{
768    MessageDirection, MessageType, MockWebSocketResponse, WebSocketConnection, WebSocketMessage,
769    WebSocketMock, WebSocketMonitor, WebSocketMonitorBuilder, WebSocketState,
770};
771
772/// Prelude for convenient imports
773pub mod prelude {
774    pub use super::accessibility::*;
775    pub use super::animation::{
776        sample_easing, verify_easing, verify_events, verify_timeline, AnimationEvent,
777        AnimationEventType, AnimationReport, AnimationTimeline, AnimationVerdict, EasingFunction,
778        EasingVerification, EventResult, Keyframe, ObservedEvent,
779    };
780    pub use super::assertion::*;
781    pub use super::audio_quality::{
782        analyze_audio, analyze_samples, detect_clipping, detect_silence, AudioLevels,
783        AudioQualityConfig, AudioQualityReport, AudioVerdict, ClippingReport, SilenceRegion,
784        SilenceReport,
785    };
786    pub use super::av_sync::{
787        compare_edl_to_onsets, default_edl_path, detect_onsets, extract_audio, AudioOnset,
788        AudioTickPlacement, AvSyncReport, DetectionConfig, EditDecision, EditDecisionList,
789        SegmentSyncResult, SyncVerdict, TickDelta,
790    };
791    pub use super::video_quality::{
792        build_ffprobe_args, parse_ffprobe_json, probe_video, validate_video, VideoCheck,
793        VideoExpectations, VideoProbe, VideoQualityReport, VideoVerdict,
794    };
795    // Brick Architecture (PROBAR-SPEC-009)
796    pub use super::brick::*;
797    pub use super::brick_house::*;
798    pub use super::bridge::*;
799    pub use super::browser::*;
800    pub use super::capabilities::*;
801    pub use super::clock::*;
802    pub use super::context::*;
803    pub use super::dialog::*;
804    pub use super::driver::*;
805    pub use super::event::*;
806    pub use super::file_ops::*;
807    pub use super::fixture::*;
808    pub use super::fuzzer::*;
809    pub use super::gpu_pixels::*;
810    pub use super::har::*;
811    pub use super::harness::*;
812    pub use super::locator::*;
813    pub use super::network::*;
814    pub use super::page_object::*;
815    pub use super::perf::*;
816    pub use super::performance::*;
817    #[cfg(feature = "media")]
818    pub use super::pixel_coverage::*;
819    pub use super::replay::*;
820    pub use super::reporter::*;
821    pub use super::result::*;
822    pub use super::runner::*;
823    pub use super::runtime::*;
824    pub use super::shard::*;
825    pub use super::simulation::*;
826    pub use super::snapshot::*;
827    // Note: strict::ConsoleMessage conflicts with tracing_support::ConsoleMessage
828    // Use explicit imports instead of glob
829    pub use super::strict::{
830        ChecklistError, ConsoleCapture, ConsoleSeverity, ConsoleValidationError, E2ETestChecklist,
831        WasmStrictMode,
832    };
833    pub use super::tracing_support::*;
834    #[cfg(feature = "tui")]
835    pub use super::tui::*;
836    pub use super::tui_load::{
837        ComponentTimings, DataGenerator, IntegrationLoadTest, SyntheticItem, TuiFrameMetrics,
838        TuiLoadAssertion, TuiLoadConfig, TuiLoadError, TuiLoadResult, TuiLoadTest,
839    };
840    pub use super::ux_coverage::*;
841    pub use super::validators::*;
842    #[cfg(feature = "media")]
843    pub use super::visual_regression::*;
844    pub use super::worker_harness::*;
845    pub use super::zero_js::*;
846    // WASM Threading Testing (PROBAR-SPEC-WASM-001)
847    pub use super::comply::*;
848    pub use super::lint::*;
849    pub use super::mock::*;
850    // Docker module types are exported with Docker prefix to avoid conflicts
851    #[cfg(feature = "docker")]
852    pub use super::docker::{
853        check_shared_array_buffer_support, validate_coop_coep_headers, Browser as DockerBrowser,
854        ContainerConfig, ContainerState, CoopCoepConfig, DockerConfig, DockerError, DockerResult,
855        DockerTestRunner, DockerTestRunnerBuilder, ParallelRunner, ParallelRunnerBuilder,
856        TestResult as DockerTestResult, TestResults as DockerTestResults,
857    };
858    #[cfg(feature = "llm")]
859    pub use super::llm::*;
860    pub use super::wait::{
861        wait_timeout, wait_until, FnCondition, LoadState, NavigationOptions, PageEvent,
862        WaitCondition, WaitOptions, WaitResult, Waiter, DEFAULT_WAIT_TIMEOUT_MS,
863        NETWORK_IDLE_THRESHOLD_MS,
864    };
865    #[cfg(all(not(target_arch = "wasm32"), feature = "watch"))]
866    pub use super::watch::*;
867    pub use super::web::*;
868    pub use super::websocket::*;
869    // Note: renacer_integration types are available as RenacerTracingConfig, etc.
870    // to avoid conflicts with tracing_support::TracingConfig
871    pub use super::renacer_integration::{
872        ChromeTrace as RenacerChromeTrace, ChromeTraceEvent, TraceCollector, TraceContext,
873        TraceSpan, TracingConfig as RenacerTracingConfig,
874    };
875}
876
877/// Standard invariants for game testing
878pub mod standard_invariants {
879    pub use super::fuzzer::standard_invariants::*;
880}
881
882// Re-export derive macros when the `derive` feature is enabled (Phase 4: Poka-Yoke)
883#[cfg(feature = "derive")]
884pub use jugar_probar_derive::{probar_test, ProbarComponent, ProbarEntity, ProbarSelector};
885
886#[cfg(test)]
887#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
888mod tests {
889    use super::*;
890
891    // ========================================================================
892    // EXTREME TDD: Tests written FIRST per spec Section 6.1
893    // ========================================================================
894
895    mod browser_tests {
896        use super::*;
897
898        #[test]
899        fn test_browser_config_defaults() {
900            let config = BrowserConfig::default();
901            assert!(config.headless);
902            assert_eq!(config.viewport_width, 800);
903            assert_eq!(config.viewport_height, 600);
904        }
905
906        #[test]
907        fn test_browser_config_builder() {
908            let config = BrowserConfig::default()
909                .with_viewport(1024, 768)
910                .with_headless(false);
911            assert!(!config.headless);
912            assert_eq!(config.viewport_width, 1024);
913            assert_eq!(config.viewport_height, 768);
914        }
915    }
916
917    mod touch_tests {
918        use super::*;
919
920        #[test]
921        fn test_touch_tap() {
922            let touch = Touch::tap(100.0, 200.0);
923            assert!((touch.x - 100.0).abs() < f32::EPSILON);
924            assert!((touch.y - 200.0).abs() < f32::EPSILON);
925            assert!(matches!(touch.action, TouchAction::Tap));
926        }
927
928        #[test]
929        fn test_touch_swipe() {
930            let touch = Touch::swipe(0.0, 0.0, 100.0, 0.0, 300);
931            assert!(matches!(touch.action, TouchAction::Swipe { .. }));
932        }
933
934        #[test]
935        fn test_touch_hold() {
936            let touch = Touch::hold(50.0, 50.0, 500);
937            assert!(matches!(touch.action, TouchAction::Hold { .. }));
938        }
939    }
940
941    mod assertion_tests {
942        use super::*;
943
944        #[test]
945        fn test_assertion_result_pass() {
946            let result = AssertionResult::pass();
947            assert!(result.passed);
948            assert!(result.message.is_empty());
949        }
950
951        #[test]
952        fn test_assertion_result_fail() {
953            let result = AssertionResult::fail("test error message");
954            assert!(!result.passed);
955            assert_eq!(result.message, "test error message");
956        }
957
958        #[test]
959        fn test_assertion_equals_pass() {
960            let result = Assertion::equals(&42, &42);
961            assert!(result.passed);
962        }
963
964        #[test]
965        fn test_assertion_equals_fail() {
966            let result = Assertion::equals(&42, &43);
967            assert!(!result.passed);
968            assert!(result.message.contains("expected"));
969        }
970
971        #[test]
972        fn test_assertion_contains_pass() {
973            let result = Assertion::contains("hello world", "world");
974            assert!(result.passed);
975        }
976
977        #[test]
978        fn test_assertion_contains_fail() {
979            let result = Assertion::contains("hello world", "foo");
980            assert!(!result.passed);
981            assert!(result.message.contains("contain"));
982        }
983
984        #[test]
985        fn test_assertion_in_range_pass() {
986            let result = Assertion::in_range(5.0, 0.0, 10.0);
987            assert!(result.passed);
988        }
989
990        #[test]
991        fn test_assertion_in_range_fail() {
992            let result = Assertion::in_range(15.0, 0.0, 10.0);
993            assert!(!result.passed);
994            assert!(result.message.contains("range"));
995        }
996
997        #[test]
998        fn test_assertion_in_range_at_boundaries() {
999            // At min boundary
1000            let result = Assertion::in_range(0.0, 0.0, 10.0);
1001            assert!(result.passed);
1002            // At max boundary
1003            let result = Assertion::in_range(10.0, 0.0, 10.0);
1004            assert!(result.passed);
1005        }
1006
1007        #[test]
1008        fn test_assertion_is_true_pass() {
1009            let result = Assertion::is_true(true, "should be true");
1010            assert!(result.passed);
1011        }
1012
1013        #[test]
1014        fn test_assertion_is_true_fail() {
1015            let result = Assertion::is_true(false, "expected true");
1016            assert!(!result.passed);
1017            assert_eq!(result.message, "expected true");
1018        }
1019
1020        #[test]
1021        fn test_assertion_is_false_pass() {
1022            let result = Assertion::is_false(false, "should be false");
1023            assert!(result.passed);
1024        }
1025
1026        #[test]
1027        fn test_assertion_is_false_fail() {
1028            let result = Assertion::is_false(true, "expected false");
1029            assert!(!result.passed);
1030            assert_eq!(result.message, "expected false");
1031        }
1032
1033        #[test]
1034        fn test_assertion_is_some_pass() {
1035            let opt = Some(42);
1036            let result = Assertion::is_some(&opt);
1037            assert!(result.passed);
1038        }
1039
1040        #[test]
1041        fn test_assertion_is_some_fail() {
1042            let opt: Option<i32> = None;
1043            let result = Assertion::is_some(&opt);
1044            assert!(!result.passed);
1045            assert!(result.message.contains("None"));
1046        }
1047
1048        #[test]
1049        fn test_assertion_is_none_pass() {
1050            let opt: Option<i32> = None;
1051            let result = Assertion::is_none(&opt);
1052            assert!(result.passed);
1053        }
1054
1055        #[test]
1056        fn test_assertion_is_none_fail() {
1057            let opt = Some(42);
1058            let result = Assertion::is_none(&opt);
1059            assert!(!result.passed);
1060            assert!(result.message.contains("Some"));
1061        }
1062
1063        #[test]
1064        fn test_assertion_is_ok_pass() {
1065            let res: Result<i32, &str> = Ok(42);
1066            let result = Assertion::is_ok(&res);
1067            assert!(result.passed);
1068        }
1069
1070        #[test]
1071        fn test_assertion_is_ok_fail() {
1072            let res: Result<i32, &str> = Err("error");
1073            let result = Assertion::is_ok(&res);
1074            assert!(!result.passed);
1075            assert!(result.message.contains("Err"));
1076        }
1077
1078        #[test]
1079        fn test_assertion_is_err_pass() {
1080            let res: Result<i32, &str> = Err("error");
1081            let result = Assertion::is_err(&res);
1082            assert!(result.passed);
1083        }
1084
1085        #[test]
1086        fn test_assertion_is_err_fail() {
1087            let res: Result<i32, &str> = Ok(42);
1088            let result = Assertion::is_err(&res);
1089            assert!(!result.passed);
1090            assert!(result.message.contains("Ok"));
1091        }
1092
1093        #[test]
1094        fn test_assertion_approx_eq_pass() {
1095            let result = Assertion::approx_eq(1.0, 1.0001, 0.01);
1096            assert!(result.passed);
1097        }
1098
1099        #[test]
1100        fn test_assertion_approx_eq_fail() {
1101            let result = Assertion::approx_eq(1.0, 2.0, 0.01);
1102            assert!(!result.passed);
1103            assert!(result.message.contains("≈"));
1104        }
1105
1106        #[test]
1107        fn test_assertion_has_length_pass() {
1108            let data = vec![1, 2, 3, 4, 5];
1109            let result = Assertion::has_length(&data, 5);
1110            assert!(result.passed);
1111        }
1112
1113        #[test]
1114        fn test_assertion_has_length_fail() {
1115            let data = vec![1, 2, 3];
1116            let result = Assertion::has_length(&data, 5);
1117            assert!(!result.passed);
1118            assert!(result.message.contains("length"));
1119        }
1120
1121        #[test]
1122        fn test_assertion_has_length_empty() {
1123            let data: Vec<i32> = vec![];
1124            let result = Assertion::has_length(&data, 0);
1125            assert!(result.passed);
1126        }
1127    }
1128
1129    mod snapshot_tests {
1130        use super::*;
1131
1132        #[test]
1133        fn test_snapshot_creation() {
1134            let snapshot = Snapshot::new("test-snapshot", vec![0, 1, 2, 3]);
1135            assert_eq!(snapshot.name, "test-snapshot");
1136            assert_eq!(snapshot.data.len(), 4);
1137            assert_eq!(snapshot.width, 0);
1138            assert_eq!(snapshot.height, 0);
1139        }
1140
1141        #[test]
1142        fn test_snapshot_with_dimensions() {
1143            let snapshot = Snapshot::new("test", vec![1, 2, 3, 4]).with_dimensions(800, 600);
1144            assert_eq!(snapshot.width, 800);
1145            assert_eq!(snapshot.height, 600);
1146        }
1147
1148        #[test]
1149        fn test_snapshot_size() {
1150            let snapshot = Snapshot::new("test", vec![1, 2, 3, 4, 5]);
1151            assert_eq!(snapshot.size(), 5);
1152        }
1153
1154        #[test]
1155        fn test_snapshot_diff_identical() {
1156            let snap1 = Snapshot::new("test", vec![1, 2, 3]);
1157            let snap2 = Snapshot::new("test", vec![1, 2, 3]);
1158            let diff = snap1.diff(&snap2);
1159            assert!(diff.is_identical());
1160            assert_eq!(diff.difference_count, 0);
1161            assert!((diff.difference_percent - 0.0).abs() < f64::EPSILON);
1162        }
1163
1164        #[test]
1165        fn test_snapshot_diff_different() {
1166            let snap1 = Snapshot::new("test", vec![1, 2, 3]);
1167            let snap2 = Snapshot::new("test", vec![1, 2, 4]);
1168            let diff = snap1.diff(&snap2);
1169            assert!(!diff.is_identical());
1170            assert_eq!(diff.difference_count, 1);
1171        }
1172
1173        #[test]
1174        fn test_snapshot_diff_empty() {
1175            let snap1 = Snapshot::new("test", vec![]);
1176            let snap2 = Snapshot::new("test", vec![]);
1177            let diff = snap1.diff(&snap2);
1178            assert!(diff.is_identical());
1179            assert!((diff.difference_percent - 0.0).abs() < f64::EPSILON);
1180        }
1181
1182        #[test]
1183        fn test_snapshot_diff_different_lengths() {
1184            let snap1 = Snapshot::new("test", vec![1, 2, 3]);
1185            let snap2 = Snapshot::new("test", vec![1, 2, 3, 4, 5]);
1186            let diff = snap1.diff(&snap2);
1187            assert!(!diff.is_identical());
1188            // Missing bytes count as differences
1189            assert_eq!(diff.difference_count, 2);
1190        }
1191
1192        #[test]
1193        fn test_snapshot_diff_within_threshold() {
1194            let snap1 = Snapshot::new("test", vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1195            let snap2 = Snapshot::new("test", vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 11]);
1196            let diff = snap1.diff(&snap2);
1197            // 1 difference out of 10 = 10%
1198            assert!(diff.within_threshold(0.1)); // 10% threshold
1199            assert!(!diff.within_threshold(0.05)); // 5% threshold
1200        }
1201
1202        #[test]
1203        fn test_snapshot_config_default() {
1204            let config = SnapshotConfig::default();
1205            assert!(!config.update_snapshots);
1206            assert!((config.threshold - 0.01).abs() < f64::EPSILON);
1207            assert_eq!(config.snapshot_dir, "__snapshots__");
1208        }
1209
1210        #[test]
1211        fn test_snapshot_config_with_update() {
1212            let config = SnapshotConfig::default().with_update(true);
1213            assert!(config.update_snapshots);
1214        }
1215
1216        #[test]
1217        fn test_snapshot_config_with_threshold() {
1218            let config = SnapshotConfig::default().with_threshold(0.05);
1219            assert!((config.threshold - 0.05).abs() < f64::EPSILON);
1220        }
1221
1222        #[test]
1223        fn test_snapshot_config_with_dir() {
1224            let config = SnapshotConfig::default().with_dir("custom_snapshots");
1225            assert_eq!(config.snapshot_dir, "custom_snapshots");
1226        }
1227
1228        #[test]
1229        fn test_snapshot_config_chained_builders() {
1230            let config = SnapshotConfig::default()
1231                .with_update(true)
1232                .with_threshold(0.02)
1233                .with_dir("my_snaps");
1234            assert!(config.update_snapshots);
1235            assert!((config.threshold - 0.02).abs() < f64::EPSILON);
1236            assert_eq!(config.snapshot_dir, "my_snaps");
1237        }
1238    }
1239
1240    mod harness_tests {
1241        use super::*;
1242        use harness::{SuiteResults, TestCase};
1243        use std::time::Duration;
1244
1245        #[test]
1246        fn test_test_suite_creation() {
1247            let suite = TestSuite::new("Game Tests");
1248            assert_eq!(suite.name, "Game Tests");
1249            assert!(suite.tests.is_empty());
1250        }
1251
1252        #[test]
1253        fn test_test_suite_add_test() {
1254            let mut suite = TestSuite::new("Suite");
1255            suite.add_test(TestCase::new("test1"));
1256            suite.add_test(TestCase::new("test2"));
1257            assert_eq!(suite.test_count(), 2);
1258        }
1259
1260        #[test]
1261        fn test_test_case_creation() {
1262            let case = TestCase::new("my_test");
1263            assert_eq!(case.name, "my_test");
1264            assert_eq!(case.timeout_ms, 30000); // default timeout
1265        }
1266
1267        #[test]
1268        fn test_test_case_with_timeout() {
1269            let case = TestCase::new("my_test").with_timeout(5000);
1270            assert_eq!(case.timeout_ms, 5000);
1271        }
1272
1273        #[test]
1274        fn test_test_result_pass() {
1275            let result = TestResult::pass("test_example");
1276            assert!(result.passed);
1277            assert_eq!(result.name, "test_example");
1278            assert!(result.error.is_none());
1279            assert_eq!(result.duration, Duration::ZERO);
1280        }
1281
1282        #[test]
1283        fn test_test_result_fail() {
1284            let result = TestResult::fail("test_example", "assertion failed");
1285            assert!(!result.passed);
1286            assert!(result.error.is_some());
1287            assert_eq!(result.error.unwrap(), "assertion failed");
1288        }
1289
1290        #[test]
1291        fn test_test_result_with_duration() {
1292            let result = TestResult::pass("test").with_duration(Duration::from_millis(100));
1293            assert_eq!(result.duration, Duration::from_millis(100));
1294        }
1295
1296        #[test]
1297        fn test_suite_results_all_passed() {
1298            let results = SuiteResults {
1299                suite_name: "test".to_string(),
1300                results: vec![TestResult::pass("test1"), TestResult::pass("test2")],
1301                duration: Duration::ZERO,
1302            };
1303            assert!(results.all_passed());
1304        }
1305
1306        #[test]
1307        fn test_suite_results_not_all_passed() {
1308            let results = SuiteResults {
1309                suite_name: "test".to_string(),
1310                results: vec![
1311                    TestResult::pass("test1"),
1312                    TestResult::fail("test2", "error"),
1313                ],
1314                duration: Duration::ZERO,
1315            };
1316            assert!(!results.all_passed());
1317        }
1318
1319        #[test]
1320        fn test_suite_results_counts() {
1321            let results = SuiteResults {
1322                suite_name: "test".to_string(),
1323                results: vec![
1324                    TestResult::pass("test1"),
1325                    TestResult::fail("test2", "error"),
1326                    TestResult::pass("test3"),
1327                ],
1328                duration: Duration::ZERO,
1329            };
1330            assert_eq!(results.passed_count(), 2);
1331            assert_eq!(results.failed_count(), 1);
1332            assert_eq!(results.total(), 3);
1333        }
1334
1335        #[test]
1336        fn test_suite_results_failures() {
1337            let results = SuiteResults {
1338                suite_name: "test".to_string(),
1339                results: vec![
1340                    TestResult::pass("test1"),
1341                    TestResult::fail("test2", "error2"),
1342                    TestResult::fail("test3", "error3"),
1343                ],
1344                duration: Duration::ZERO,
1345            };
1346            let failures = results.failures();
1347            assert_eq!(failures.len(), 2);
1348            assert_eq!(failures[0].name, "test2");
1349            assert_eq!(failures[1].name, "test3");
1350        }
1351
1352        #[test]
1353        fn test_harness_run_empty_suite() {
1354            let harness = TestHarness::new();
1355            let suite = TestSuite::new("Empty");
1356            let results = harness.run(&suite);
1357            assert!(results.all_passed());
1358            assert_eq!(results.total(), 0);
1359        }
1360
1361        #[test]
1362        fn test_harness_with_fail_fast() {
1363            let harness = TestHarness::new().with_fail_fast();
1364            assert!(harness.fail_fast);
1365        }
1366
1367        #[test]
1368        fn test_harness_with_parallel() {
1369            let harness = TestHarness::new().with_parallel();
1370            assert!(harness.parallel);
1371        }
1372
1373        #[test]
1374        fn test_harness_default() {
1375            let harness = TestHarness::default();
1376            assert!(!harness.fail_fast);
1377            assert!(!harness.parallel);
1378        }
1379    }
1380
1381    mod input_event_tests {
1382        use super::*;
1383
1384        #[test]
1385        fn test_input_event_touch() {
1386            let event = InputEvent::touch(100.0, 200.0);
1387            assert!(
1388                matches!(event, InputEvent::Touch { x, y } if (x - 100.0).abs() < f32::EPSILON && (y - 200.0).abs() < f32::EPSILON)
1389            );
1390        }
1391
1392        #[test]
1393        fn test_input_event_key_press() {
1394            let event = InputEvent::key_press("ArrowUp");
1395            assert!(matches!(event, InputEvent::KeyPress { key } if key == "ArrowUp"));
1396        }
1397
1398        #[test]
1399        fn test_input_event_key_release() {
1400            let event = InputEvent::key_release("Space");
1401            assert!(matches!(event, InputEvent::KeyRelease { key } if key == "Space"));
1402        }
1403
1404        #[test]
1405        fn test_input_event_mouse_click() {
1406            let event = InputEvent::mouse_click(50.0, 75.0);
1407            assert!(
1408                matches!(event, InputEvent::MouseClick { x, y } if (x - 50.0).abs() < f32::EPSILON && (y - 75.0).abs() < f32::EPSILON)
1409            );
1410        }
1411
1412        #[test]
1413        fn test_input_event_mouse_move() {
1414            let event = InputEvent::mouse_move(150.0, 250.0);
1415            assert!(
1416                matches!(event, InputEvent::MouseMove { x, y } if (x - 150.0).abs() < f32::EPSILON && (y - 250.0).abs() < f32::EPSILON)
1417            );
1418        }
1419
1420        #[test]
1421        fn test_input_event_gamepad_button_pressed() {
1422            let event = InputEvent::gamepad_button(0, true);
1423            assert!(matches!(
1424                event,
1425                InputEvent::GamepadButton {
1426                    button: 0,
1427                    pressed: true
1428                }
1429            ));
1430        }
1431
1432        #[test]
1433        fn test_input_event_gamepad_button_released() {
1434            let event = InputEvent::gamepad_button(1, false);
1435            assert!(matches!(
1436                event,
1437                InputEvent::GamepadButton {
1438                    button: 1,
1439                    pressed: false
1440                }
1441            ));
1442        }
1443
1444        #[test]
1445        fn test_touch_tap_coordinates() {
1446            let touch = Touch::tap(100.0, 200.0);
1447            assert!((touch.x - 100.0).abs() < f32::EPSILON);
1448            assert!((touch.y - 200.0).abs() < f32::EPSILON);
1449            assert!(matches!(touch.action, TouchAction::Tap));
1450        }
1451
1452        #[test]
1453        fn test_touch_swipe_full_properties() {
1454            let touch = Touch::swipe(10.0, 20.0, 100.0, 200.0, 300);
1455            assert!((touch.x - 10.0).abs() < f32::EPSILON);
1456            assert!((touch.y - 20.0).abs() < f32::EPSILON);
1457            match touch.action {
1458                TouchAction::Swipe {
1459                    end_x,
1460                    end_y,
1461                    duration_ms,
1462                } => {
1463                    assert!((end_x - 100.0).abs() < f32::EPSILON);
1464                    assert!((end_y - 200.0).abs() < f32::EPSILON);
1465                    assert_eq!(duration_ms, 300);
1466                }
1467                _ => panic!("expected Swipe action"),
1468            }
1469        }
1470
1471        #[test]
1472        fn test_touch_hold_full_properties() {
1473            let touch = Touch::hold(50.0, 60.0, 500);
1474            assert!((touch.x - 50.0).abs() < f32::EPSILON);
1475            assert!((touch.y - 60.0).abs() < f32::EPSILON);
1476            match touch.action {
1477                TouchAction::Hold { duration_ms } => {
1478                    assert_eq!(duration_ms, 500);
1479                }
1480                _ => panic!("expected Hold action"),
1481            }
1482        }
1483
1484        #[test]
1485        fn test_touch_action_equality() {
1486            assert_eq!(TouchAction::Tap, TouchAction::Tap);
1487            let swipe1 = TouchAction::Swipe {
1488                end_x: 1.0,
1489                end_y: 2.0,
1490                duration_ms: 100,
1491            };
1492            let swipe2 = TouchAction::Swipe {
1493                end_x: 1.0,
1494                end_y: 2.0,
1495                duration_ms: 100,
1496            };
1497            assert_eq!(swipe1, swipe2);
1498            let hold1 = TouchAction::Hold { duration_ms: 500 };
1499            let hold2 = TouchAction::Hold { duration_ms: 500 };
1500            assert_eq!(hold1, hold2);
1501        }
1502
1503        #[test]
1504        fn test_touch_equality() {
1505            let t1 = Touch::tap(100.0, 200.0);
1506            let t2 = Touch::tap(100.0, 200.0);
1507            assert_eq!(t1, t2);
1508        }
1509
1510        #[test]
1511        fn test_input_event_equality() {
1512            let e1 = InputEvent::touch(10.0, 20.0);
1513            let e2 = InputEvent::touch(10.0, 20.0);
1514            assert_eq!(e1, e2);
1515        }
1516    }
1517
1518    mod error_tests {
1519        use super::*;
1520
1521        #[test]
1522        fn test_probar_error_display() {
1523            let err = ProbarError::BrowserNotFound;
1524            let msg = err.to_string();
1525            assert!(msg.contains("browser") || msg.contains("Browser"));
1526        }
1527
1528        #[test]
1529        fn test_probar_error_timeout() {
1530            let err = ProbarError::Timeout { ms: 5000 };
1531            let msg = err.to_string();
1532            assert!(msg.contains("5000"));
1533        }
1534    }
1535}