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