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