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