Skip to main content

jugar_probar/
lib.rs

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