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