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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[cfg(any(feature = "llm-types", feature = "llm"))]
572#[allow(
573 clippy::missing_errors_doc,
574 clippy::must_use_candidate,
575 clippy::missing_const_for_fn,
576 clippy::doc_markdown
577)]
578pub mod llm;
579
580pub use accessibility::{
581 AccessibilityAudit, AccessibilityConfig, AccessibilityIssue, AccessibilityValidator, Color,
582 ContrastAnalysis, ContrastPair, FlashDetector, FlashResult, FocusConfig, KeyboardIssue,
583 Severity, MIN_CONTRAST_LARGE, MIN_CONTRAST_NORMAL, MIN_CONTRAST_UI,
584};
585pub use animation::{
586 sample_easing, verify_easing, verify_events, verify_timeline, AnimationEvent,
587 AnimationEventType, AnimationReport, AnimationTimeline, AnimationVerdict, EasingFunction,
588 EasingVerification, EventResult, Keyframe, ObservedEvent,
589};
590pub use assertion::{
591 retry_contains, retry_eq, retry_none, retry_some, retry_true, Assertion, AssertionCheckResult,
592 AssertionFailure, AssertionMode, AssertionResult, AssertionSummary, EnergyVerifier,
593 EquationContext, EquationResult, EquationVerifier, InvariantVerifier, KinematicVerifier,
594 MomentumVerifier, RetryAssertion, RetryConfig, RetryError, RetryResult, SoftAssertionError,
595 SoftAssertions, Variable,
596};
597pub use audio_quality::{
598 analyze_audio, analyze_samples, detect_clipping, detect_silence, AudioLevels,
599 AudioQualityConfig, AudioQualityReport, AudioVerdict, ClippingReport, SilenceRegion,
600 SilenceReport,
601};
602pub use av_sync::{
603 compare_edl_to_onsets, default_edl_path, detect_onsets, extract_audio, AudioOnset,
604 AudioTickPlacement, AvSyncReport, DetectionConfig, EditDecision, EditDecisionList,
605 SegmentSyncResult, SyncVerdict, TickDelta, DEFAULT_SAMPLE_RATE,
606};
607pub use bridge::{
608 BridgeConnection, DiffRegion, EntitySnapshot, GameStateData, GameStateSnapshot, SnapshotCache,
609 StateBridge, VisualDiff,
610};
611pub use browser::{Browser, BrowserConfig, BrowserConsoleLevel, BrowserConsoleMessage, Page};
612pub use capabilities::{
613 CapabilityError, CapabilityStatus, RequiredHeaders, WasmThreadCapabilities, WorkerEmulator,
614 WorkerMessage, WorkerState,
615};
616pub use cdp_coverage::{
617 CoverageConfig, CoverageRange, CoverageReport, CoveredFunction, FunctionCoverage, JsCoverage,
618 LineCoverage, ScriptCoverage, SourceMapEntry, WasmCoverage, WasmSourceMap,
619};
620#[cfg(feature = "browser")]
621pub use chromium_driver::ChromiumDriver;
622pub use clock::{
623 create_clock, Clock, ClockController, ClockError, ClockOptions, ClockState, FakeClock,
624};
625pub use context::{
626 BrowserContext, ContextConfig, ContextManager, ContextPool, ContextPoolStats, ContextState,
627 Cookie, Geolocation, SameSite, StorageState,
628};
629pub use dialog::{
630 AutoDialogBehavior, Dialog, DialogAction, DialogExpectation, DialogHandler,
631 DialogHandlerBuilder, DialogType,
632};
633#[cfg(feature = "browser")]
634pub use driver::{BrowserController, ProbarDriver};
635pub use driver::{
636 DeviceDescriptor, DriverConfig, ElementHandle, MockDriver, NetworkInterceptor, NetworkResponse,
637 PageMetrics, Screenshot,
638};
639pub use event::{InputEvent, Touch, TouchAction};
640pub use file_ops::{
641 guess_mime_type, Download, DownloadManager, DownloadState, FileChooser, FileInput,
642};
643pub use fixture::{
644 Fixture, FixtureBuilder, FixtureManager, FixtureScope, FixtureState, SimpleFixture,
645};
646pub use fuzzer::{
647 FuzzerConfig, InputFuzzer, InvariantCheck, InvariantChecker, InvariantViolation, Seed,
648};
649pub use har::{
650 Har, HarBrowser, HarCache, HarContent, HarCookie, HarCreator, HarEntry, HarError, HarHeader,
651 HarLog, HarOptions, HarPlayer, HarPostData, HarPostParam, HarQueryParam, HarRecorder,
652 HarRequest, HarResponse, HarTimings, NotFoundBehavior,
653};
654pub use harness::{TestCase, TestHarness, TestResult, TestSuite};
655pub use locator::{
656 expect, BoundingBox, DragBuilder, DragOperation, Expect, ExpectAssertion, Locator,
657 LocatorAction, LocatorOptions, LocatorQuery, Point, Selector, DEFAULT_POLL_INTERVAL_MS,
658 DEFAULT_TIMEOUT_MS,
659};
660pub use network::{
661 CapturedRequest, HttpMethod, MockResponse, NetworkInterception, NetworkInterceptionBuilder,
662 Route, UrlPattern,
663};
664pub use page_object::{
665 PageObject, PageObjectBuilder, PageObjectInfo, PageRegistry, SimplePageObject, UrlMatcher,
666};
667pub use performance::{
668 Measurement, MetricStats, MetricType, PerformanceMonitor, PerformanceProfile,
669 PerformanceProfiler, PerformanceProfilerBuilder, PerformanceSummary, PerformanceThreshold,
670};
671pub use playbook::{
672 calculate_mutation_score, check_complexity_violation, to_dot, Action as PlaybookAction,
673 ActionExecutor, Assertion as PlaybookAssertion, AssertionFailure as PlaybookAssertionFailure,
674 ComplexityAnalyzer, ComplexityClass, ComplexityResult, DeterminismInfo,
675 ExecutionResult as PlaybookExecutionResult, ExecutorError, Invariant, IssueSeverity,
676 MutantResult, MutationClass, MutationGenerator, MutationScore, PerformanceBudget, Playbook,
677 PlaybookError, PlaybookExecutor, ReachabilityInfo, State as PlaybookState, StateMachine,
678 StateMachineValidator, Transition as PlaybookTransition, ValidationIssue, ValidationResult,
679 WaitCondition as PlaybookWaitCondition,
680};
681pub use presentar::{
682 generate_falsification_playbook, parse_and_validate as parse_and_validate_presentar,
683 validate_config as validate_presentar_config, Cell as PresentarCell, Color as PresentarColor,
684 FalsificationCheck, FalsificationResult, KeybindingConfig, LayoutConfig, PanelConfig,
685 PanelConfigs, PanelType, PresentarConfig, PresentarError, TerminalAssertion, TerminalSnapshot,
686 ThemeConfig, ValidationResult as PresentarValidationResult, FALSIFICATION_COUNT,
687 SCHEMA_VERSION,
688};
689pub use renacer_integration::{
690 ChromeTrace, ChromeTraceEvent, TraceCollector, TraceContext, TraceSpan,
691 TracingConfig as RenacerTracingConfig,
692};
693pub use replay::{
694 Replay, ReplayHeader, ReplayPlayer, ReplayRecorder, StateCheckpoint, TimedInput,
695 VerificationResult, REPLAY_FORMAT_VERSION,
696};
697pub use reporter::{
698 AndonCordPulled, FailureMode, Reporter, TestResultEntry, TestStatus, TraceData,
699};
700pub use result::{ProbarError, ProbarResult};
701pub use runtime::{
702 ComponentId, EntityId, FrameResult, GameHostState, MemoryView, ProbarComponent, ProbarEntity,
703 RuntimeConfig, StateDelta, WasmRuntime,
704};
705pub use shard::{ShardConfig, ShardParseError, ShardReport, ShardedRunner};
706pub use simulation::{
707 run_replay, run_simulation, RandomWalkAgent, RecordedFrame, ReplayResult, SimulatedGameState,
708 SimulationConfig, SimulationRecording,
709};
710pub use snapshot::{Snapshot, SnapshotConfig, SnapshotDiff};
711pub use strict::{
712 ChecklistError, ConsoleCapture, ConsoleSeverity, ConsoleValidationError, E2ETestChecklist,
713 WasmStrictMode,
714};
715pub use tracing_support::{
716 ConsoleLevel, ConsoleMessage, EventCategory, EventLevel, ExecutionTracer, NetworkEvent,
717 SpanStatus, TraceArchive, TraceMetadata, TracedEvent, TracedSpan, TracingConfig,
718};
719#[cfg(feature = "tui")]
720pub use tui::{
721 expect_frame, FrameAssertion, FrameSequence, MultiValueTracker, SnapshotManager, TuiFrame,
722 TuiSnapshot, TuiTestBackend, ValueTracker,
723};
724pub use tui_load::{
725 ComponentTimings, DataGenerator, IntegrationLoadTest, SyntheticItem, TuiFrameMetrics,
726 TuiLoadAssertion, TuiLoadConfig, TuiLoadError, TuiLoadResult, TuiLoadTest,
727};
728pub use ux_coverage::{
729 calculator_coverage, game_coverage, ElementCoverage, ElementId, InteractionType, StateId,
730 TrackedInteraction, UxCoverageBuilder, UxCoverageReport, UxCoverageTracker,
731};
732pub use validators::{
733 CompressionAlgorithm, PartialResult, ScreenshotContent, StateTransition, StreamingMetric,
734 StreamingMetricRecord, StreamingState, StreamingUxValidator, StreamingValidationError,
735 StreamingValidationResult, TestExecutionStats, VuMeterConfig, VuMeterError, VuMeterSample,
736};
737pub use video_quality::{
738 build_ffprobe_args, parse_ffprobe_json, probe_video, validate_video, VideoCheck,
739 VideoExpectations, VideoProbe, VideoQualityReport, VideoVerdict,
740};
741#[cfg(feature = "media")]
742pub use visual_regression::{
743 perceptual_diff, ImageDiffResult, MaskRegion, ScreenshotComparison, VisualRegressionConfig,
744 VisualRegressionTester,
745};
746pub use wait::{
747 wait_timeout, wait_until, FnCondition, LoadState, NavigationOptions, PageEvent, WaitCondition,
748 WaitOptions, WaitResult, Waiter, DEFAULT_WAIT_TIMEOUT_MS, NETWORK_IDLE_THRESHOLD_MS,
749};
750#[cfg(all(not(target_arch = "wasm32"), feature = "watch"))]
751pub use watch::{
752 FileChange, FileChangeKind, FileWatcher, FnWatchHandler, WatchBuilder, WatchConfig,
753 WatchHandler, WatchStats,
754};
755pub use brick::{
757 Brick, BrickAssertion, BrickBudget, BrickError, BrickPhase, BrickResult, BrickVerification,
758 BudgetViolation,
759};
760pub use brick::{
762 AudioBrick, AudioParam, BrickWorkerMessage, BrickWorkerMessageDirection, EventBinding,
763 EventBrick, EventHandler, EventType, FieldType, RingBufferConfig, WorkerBrick,
764 WorkerTransition,
765};
766pub use brick_house::{BrickHouse, BrickHouseBuilder, BrickTiming, BudgetReport, JidokaAlert};
767pub use websocket::{
768 MessageDirection, MessageType, MockWebSocketResponse, WebSocketConnection, WebSocketMessage,
769 WebSocketMock, WebSocketMonitor, WebSocketMonitorBuilder, WebSocketState,
770};
771
772pub mod prelude {
774 pub use super::accessibility::*;
775 pub use super::animation::{
776 sample_easing, verify_easing, verify_events, verify_timeline, AnimationEvent,
777 AnimationEventType, AnimationReport, AnimationTimeline, AnimationVerdict, EasingFunction,
778 EasingVerification, EventResult, Keyframe, ObservedEvent,
779 };
780 pub use super::assertion::*;
781 pub use super::audio_quality::{
782 analyze_audio, analyze_samples, detect_clipping, detect_silence, AudioLevels,
783 AudioQualityConfig, AudioQualityReport, AudioVerdict, ClippingReport, SilenceRegion,
784 SilenceReport,
785 };
786 pub use super::av_sync::{
787 compare_edl_to_onsets, default_edl_path, detect_onsets, extract_audio, AudioOnset,
788 AudioTickPlacement, AvSyncReport, DetectionConfig, EditDecision, EditDecisionList,
789 SegmentSyncResult, SyncVerdict, TickDelta,
790 };
791 pub use super::video_quality::{
792 build_ffprobe_args, parse_ffprobe_json, probe_video, validate_video, VideoCheck,
793 VideoExpectations, VideoProbe, VideoQualityReport, VideoVerdict,
794 };
795 pub use super::brick::*;
797 pub use super::brick_house::*;
798 pub use super::bridge::*;
799 pub use super::browser::*;
800 pub use super::capabilities::*;
801 pub use super::clock::*;
802 pub use super::context::*;
803 pub use super::dialog::*;
804 pub use super::driver::*;
805 pub use super::event::*;
806 pub use super::file_ops::*;
807 pub use super::fixture::*;
808 pub use super::fuzzer::*;
809 pub use super::gpu_pixels::*;
810 pub use super::har::*;
811 pub use super::harness::*;
812 pub use super::locator::*;
813 pub use super::network::*;
814 pub use super::page_object::*;
815 pub use super::perf::*;
816 pub use super::performance::*;
817 #[cfg(feature = "media")]
818 pub use super::pixel_coverage::*;
819 pub use super::replay::*;
820 pub use super::reporter::*;
821 pub use super::result::*;
822 pub use super::runner::*;
823 pub use super::runtime::*;
824 pub use super::shard::*;
825 pub use super::simulation::*;
826 pub use super::snapshot::*;
827 pub use super::strict::{
830 ChecklistError, ConsoleCapture, ConsoleSeverity, ConsoleValidationError, E2ETestChecklist,
831 WasmStrictMode,
832 };
833 pub use super::tracing_support::*;
834 #[cfg(feature = "tui")]
835 pub use super::tui::*;
836 pub use super::tui_load::{
837 ComponentTimings, DataGenerator, IntegrationLoadTest, SyntheticItem, TuiFrameMetrics,
838 TuiLoadAssertion, TuiLoadConfig, TuiLoadError, TuiLoadResult, TuiLoadTest,
839 };
840 pub use super::ux_coverage::*;
841 pub use super::validators::*;
842 #[cfg(feature = "media")]
843 pub use super::visual_regression::*;
844 pub use super::worker_harness::*;
845 pub use super::zero_js::*;
846 pub use super::comply::*;
848 pub use super::lint::*;
849 pub use super::mock::*;
850 #[cfg(feature = "docker")]
852 pub use super::docker::{
853 check_shared_array_buffer_support, validate_coop_coep_headers, Browser as DockerBrowser,
854 ContainerConfig, ContainerState, CoopCoepConfig, DockerConfig, DockerError, DockerResult,
855 DockerTestRunner, DockerTestRunnerBuilder, ParallelRunner, ParallelRunnerBuilder,
856 TestResult as DockerTestResult, TestResults as DockerTestResults,
857 };
858 #[cfg(feature = "llm")]
859 pub use super::llm::*;
860 pub use super::wait::{
861 wait_timeout, wait_until, FnCondition, LoadState, NavigationOptions, PageEvent,
862 WaitCondition, WaitOptions, WaitResult, Waiter, DEFAULT_WAIT_TIMEOUT_MS,
863 NETWORK_IDLE_THRESHOLD_MS,
864 };
865 #[cfg(all(not(target_arch = "wasm32"), feature = "watch"))]
866 pub use super::watch::*;
867 pub use super::web::*;
868 pub use super::websocket::*;
869 pub use super::renacer_integration::{
872 ChromeTrace as RenacerChromeTrace, ChromeTraceEvent, TraceCollector, TraceContext,
873 TraceSpan, TracingConfig as RenacerTracingConfig,
874 };
875}
876
877pub mod standard_invariants {
879 pub use super::fuzzer::standard_invariants::*;
880}
881
882#[cfg(feature = "derive")]
884pub use jugar_probar_derive::{probar_test, ProbarComponent, ProbarEntity, ProbarSelector};
885
886#[cfg(test)]
887#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
888mod tests {
889 use super::*;
890
891 mod browser_tests {
896 use super::*;
897
898 #[test]
899 fn test_browser_config_defaults() {
900 let config = BrowserConfig::default();
901 assert!(config.headless);
902 assert_eq!(config.viewport_width, 800);
903 assert_eq!(config.viewport_height, 600);
904 }
905
906 #[test]
907 fn test_browser_config_builder() {
908 let config = BrowserConfig::default()
909 .with_viewport(1024, 768)
910 .with_headless(false);
911 assert!(!config.headless);
912 assert_eq!(config.viewport_width, 1024);
913 assert_eq!(config.viewport_height, 768);
914 }
915 }
916
917 mod touch_tests {
918 use super::*;
919
920 #[test]
921 fn test_touch_tap() {
922 let touch = Touch::tap(100.0, 200.0);
923 assert!((touch.x - 100.0).abs() < f32::EPSILON);
924 assert!((touch.y - 200.0).abs() < f32::EPSILON);
925 assert!(matches!(touch.action, TouchAction::Tap));
926 }
927
928 #[test]
929 fn test_touch_swipe() {
930 let touch = Touch::swipe(0.0, 0.0, 100.0, 0.0, 300);
931 assert!(matches!(touch.action, TouchAction::Swipe { .. }));
932 }
933
934 #[test]
935 fn test_touch_hold() {
936 let touch = Touch::hold(50.0, 50.0, 500);
937 assert!(matches!(touch.action, TouchAction::Hold { .. }));
938 }
939 }
940
941 mod assertion_tests {
942 use super::*;
943
944 #[test]
945 fn test_assertion_result_pass() {
946 let result = AssertionResult::pass();
947 assert!(result.passed);
948 assert!(result.message.is_empty());
949 }
950
951 #[test]
952 fn test_assertion_result_fail() {
953 let result = AssertionResult::fail("test error message");
954 assert!(!result.passed);
955 assert_eq!(result.message, "test error message");
956 }
957
958 #[test]
959 fn test_assertion_equals_pass() {
960 let result = Assertion::equals(&42, &42);
961 assert!(result.passed);
962 }
963
964 #[test]
965 fn test_assertion_equals_fail() {
966 let result = Assertion::equals(&42, &43);
967 assert!(!result.passed);
968 assert!(result.message.contains("expected"));
969 }
970
971 #[test]
972 fn test_assertion_contains_pass() {
973 let result = Assertion::contains("hello world", "world");
974 assert!(result.passed);
975 }
976
977 #[test]
978 fn test_assertion_contains_fail() {
979 let result = Assertion::contains("hello world", "foo");
980 assert!(!result.passed);
981 assert!(result.message.contains("contain"));
982 }
983
984 #[test]
985 fn test_assertion_in_range_pass() {
986 let result = Assertion::in_range(5.0, 0.0, 10.0);
987 assert!(result.passed);
988 }
989
990 #[test]
991 fn test_assertion_in_range_fail() {
992 let result = Assertion::in_range(15.0, 0.0, 10.0);
993 assert!(!result.passed);
994 assert!(result.message.contains("range"));
995 }
996
997 #[test]
998 fn test_assertion_in_range_at_boundaries() {
999 let result = Assertion::in_range(0.0, 0.0, 10.0);
1001 assert!(result.passed);
1002 let result = Assertion::in_range(10.0, 0.0, 10.0);
1004 assert!(result.passed);
1005 }
1006
1007 #[test]
1008 fn test_assertion_is_true_pass() {
1009 let result = Assertion::is_true(true, "should be true");
1010 assert!(result.passed);
1011 }
1012
1013 #[test]
1014 fn test_assertion_is_true_fail() {
1015 let result = Assertion::is_true(false, "expected true");
1016 assert!(!result.passed);
1017 assert_eq!(result.message, "expected true");
1018 }
1019
1020 #[test]
1021 fn test_assertion_is_false_pass() {
1022 let result = Assertion::is_false(false, "should be false");
1023 assert!(result.passed);
1024 }
1025
1026 #[test]
1027 fn test_assertion_is_false_fail() {
1028 let result = Assertion::is_false(true, "expected false");
1029 assert!(!result.passed);
1030 assert_eq!(result.message, "expected false");
1031 }
1032
1033 #[test]
1034 fn test_assertion_is_some_pass() {
1035 let opt = Some(42);
1036 let result = Assertion::is_some(&opt);
1037 assert!(result.passed);
1038 }
1039
1040 #[test]
1041 fn test_assertion_is_some_fail() {
1042 let opt: Option<i32> = None;
1043 let result = Assertion::is_some(&opt);
1044 assert!(!result.passed);
1045 assert!(result.message.contains("None"));
1046 }
1047
1048 #[test]
1049 fn test_assertion_is_none_pass() {
1050 let opt: Option<i32> = None;
1051 let result = Assertion::is_none(&opt);
1052 assert!(result.passed);
1053 }
1054
1055 #[test]
1056 fn test_assertion_is_none_fail() {
1057 let opt = Some(42);
1058 let result = Assertion::is_none(&opt);
1059 assert!(!result.passed);
1060 assert!(result.message.contains("Some"));
1061 }
1062
1063 #[test]
1064 fn test_assertion_is_ok_pass() {
1065 let res: Result<i32, &str> = Ok(42);
1066 let result = Assertion::is_ok(&res);
1067 assert!(result.passed);
1068 }
1069
1070 #[test]
1071 fn test_assertion_is_ok_fail() {
1072 let res: Result<i32, &str> = Err("error");
1073 let result = Assertion::is_ok(&res);
1074 assert!(!result.passed);
1075 assert!(result.message.contains("Err"));
1076 }
1077
1078 #[test]
1079 fn test_assertion_is_err_pass() {
1080 let res: Result<i32, &str> = Err("error");
1081 let result = Assertion::is_err(&res);
1082 assert!(result.passed);
1083 }
1084
1085 #[test]
1086 fn test_assertion_is_err_fail() {
1087 let res: Result<i32, &str> = Ok(42);
1088 let result = Assertion::is_err(&res);
1089 assert!(!result.passed);
1090 assert!(result.message.contains("Ok"));
1091 }
1092
1093 #[test]
1094 fn test_assertion_approx_eq_pass() {
1095 let result = Assertion::approx_eq(1.0, 1.0001, 0.01);
1096 assert!(result.passed);
1097 }
1098
1099 #[test]
1100 fn test_assertion_approx_eq_fail() {
1101 let result = Assertion::approx_eq(1.0, 2.0, 0.01);
1102 assert!(!result.passed);
1103 assert!(result.message.contains("≈"));
1104 }
1105
1106 #[test]
1107 fn test_assertion_has_length_pass() {
1108 let data = vec![1, 2, 3, 4, 5];
1109 let result = Assertion::has_length(&data, 5);
1110 assert!(result.passed);
1111 }
1112
1113 #[test]
1114 fn test_assertion_has_length_fail() {
1115 let data = vec![1, 2, 3];
1116 let result = Assertion::has_length(&data, 5);
1117 assert!(!result.passed);
1118 assert!(result.message.contains("length"));
1119 }
1120
1121 #[test]
1122 fn test_assertion_has_length_empty() {
1123 let data: Vec<i32> = vec![];
1124 let result = Assertion::has_length(&data, 0);
1125 assert!(result.passed);
1126 }
1127 }
1128
1129 mod snapshot_tests {
1130 use super::*;
1131
1132 #[test]
1133 fn test_snapshot_creation() {
1134 let snapshot = Snapshot::new("test-snapshot", vec![0, 1, 2, 3]);
1135 assert_eq!(snapshot.name, "test-snapshot");
1136 assert_eq!(snapshot.data.len(), 4);
1137 assert_eq!(snapshot.width, 0);
1138 assert_eq!(snapshot.height, 0);
1139 }
1140
1141 #[test]
1142 fn test_snapshot_with_dimensions() {
1143 let snapshot = Snapshot::new("test", vec![1, 2, 3, 4]).with_dimensions(800, 600);
1144 assert_eq!(snapshot.width, 800);
1145 assert_eq!(snapshot.height, 600);
1146 }
1147
1148 #[test]
1149 fn test_snapshot_size() {
1150 let snapshot = Snapshot::new("test", vec![1, 2, 3, 4, 5]);
1151 assert_eq!(snapshot.size(), 5);
1152 }
1153
1154 #[test]
1155 fn test_snapshot_diff_identical() {
1156 let snap1 = Snapshot::new("test", vec![1, 2, 3]);
1157 let snap2 = Snapshot::new("test", vec![1, 2, 3]);
1158 let diff = snap1.diff(&snap2);
1159 assert!(diff.is_identical());
1160 assert_eq!(diff.difference_count, 0);
1161 assert!((diff.difference_percent - 0.0).abs() < f64::EPSILON);
1162 }
1163
1164 #[test]
1165 fn test_snapshot_diff_different() {
1166 let snap1 = Snapshot::new("test", vec![1, 2, 3]);
1167 let snap2 = Snapshot::new("test", vec![1, 2, 4]);
1168 let diff = snap1.diff(&snap2);
1169 assert!(!diff.is_identical());
1170 assert_eq!(diff.difference_count, 1);
1171 }
1172
1173 #[test]
1174 fn test_snapshot_diff_empty() {
1175 let snap1 = Snapshot::new("test", vec![]);
1176 let snap2 = Snapshot::new("test", vec![]);
1177 let diff = snap1.diff(&snap2);
1178 assert!(diff.is_identical());
1179 assert!((diff.difference_percent - 0.0).abs() < f64::EPSILON);
1180 }
1181
1182 #[test]
1183 fn test_snapshot_diff_different_lengths() {
1184 let snap1 = Snapshot::new("test", vec![1, 2, 3]);
1185 let snap2 = Snapshot::new("test", vec![1, 2, 3, 4, 5]);
1186 let diff = snap1.diff(&snap2);
1187 assert!(!diff.is_identical());
1188 assert_eq!(diff.difference_count, 2);
1190 }
1191
1192 #[test]
1193 fn test_snapshot_diff_within_threshold() {
1194 let snap1 = Snapshot::new("test", vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1195 let snap2 = Snapshot::new("test", vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 11]);
1196 let diff = snap1.diff(&snap2);
1197 assert!(diff.within_threshold(0.1)); assert!(!diff.within_threshold(0.05)); }
1201
1202 #[test]
1203 fn test_snapshot_config_default() {
1204 let config = SnapshotConfig::default();
1205 assert!(!config.update_snapshots);
1206 assert!((config.threshold - 0.01).abs() < f64::EPSILON);
1207 assert_eq!(config.snapshot_dir, "__snapshots__");
1208 }
1209
1210 #[test]
1211 fn test_snapshot_config_with_update() {
1212 let config = SnapshotConfig::default().with_update(true);
1213 assert!(config.update_snapshots);
1214 }
1215
1216 #[test]
1217 fn test_snapshot_config_with_threshold() {
1218 let config = SnapshotConfig::default().with_threshold(0.05);
1219 assert!((config.threshold - 0.05).abs() < f64::EPSILON);
1220 }
1221
1222 #[test]
1223 fn test_snapshot_config_with_dir() {
1224 let config = SnapshotConfig::default().with_dir("custom_snapshots");
1225 assert_eq!(config.snapshot_dir, "custom_snapshots");
1226 }
1227
1228 #[test]
1229 fn test_snapshot_config_chained_builders() {
1230 let config = SnapshotConfig::default()
1231 .with_update(true)
1232 .with_threshold(0.02)
1233 .with_dir("my_snaps");
1234 assert!(config.update_snapshots);
1235 assert!((config.threshold - 0.02).abs() < f64::EPSILON);
1236 assert_eq!(config.snapshot_dir, "my_snaps");
1237 }
1238 }
1239
1240 mod harness_tests {
1241 use super::*;
1242 use harness::{SuiteResults, TestCase};
1243 use std::time::Duration;
1244
1245 #[test]
1246 fn test_test_suite_creation() {
1247 let suite = TestSuite::new("Game Tests");
1248 assert_eq!(suite.name, "Game Tests");
1249 assert!(suite.tests.is_empty());
1250 }
1251
1252 #[test]
1253 fn test_test_suite_add_test() {
1254 let mut suite = TestSuite::new("Suite");
1255 suite.add_test(TestCase::new("test1"));
1256 suite.add_test(TestCase::new("test2"));
1257 assert_eq!(suite.test_count(), 2);
1258 }
1259
1260 #[test]
1261 fn test_test_case_creation() {
1262 let case = TestCase::new("my_test");
1263 assert_eq!(case.name, "my_test");
1264 assert_eq!(case.timeout_ms, 30000); }
1266
1267 #[test]
1268 fn test_test_case_with_timeout() {
1269 let case = TestCase::new("my_test").with_timeout(5000);
1270 assert_eq!(case.timeout_ms, 5000);
1271 }
1272
1273 #[test]
1274 fn test_test_result_pass() {
1275 let result = TestResult::pass("test_example");
1276 assert!(result.passed);
1277 assert_eq!(result.name, "test_example");
1278 assert!(result.error.is_none());
1279 assert_eq!(result.duration, Duration::ZERO);
1280 }
1281
1282 #[test]
1283 fn test_test_result_fail() {
1284 let result = TestResult::fail("test_example", "assertion failed");
1285 assert!(!result.passed);
1286 assert!(result.error.is_some());
1287 assert_eq!(result.error.unwrap(), "assertion failed");
1288 }
1289
1290 #[test]
1291 fn test_test_result_with_duration() {
1292 let result = TestResult::pass("test").with_duration(Duration::from_millis(100));
1293 assert_eq!(result.duration, Duration::from_millis(100));
1294 }
1295
1296 #[test]
1297 fn test_suite_results_all_passed() {
1298 let results = SuiteResults {
1299 suite_name: "test".to_string(),
1300 results: vec![TestResult::pass("test1"), TestResult::pass("test2")],
1301 duration: Duration::ZERO,
1302 };
1303 assert!(results.all_passed());
1304 }
1305
1306 #[test]
1307 fn test_suite_results_not_all_passed() {
1308 let results = SuiteResults {
1309 suite_name: "test".to_string(),
1310 results: vec![
1311 TestResult::pass("test1"),
1312 TestResult::fail("test2", "error"),
1313 ],
1314 duration: Duration::ZERO,
1315 };
1316 assert!(!results.all_passed());
1317 }
1318
1319 #[test]
1320 fn test_suite_results_counts() {
1321 let results = SuiteResults {
1322 suite_name: "test".to_string(),
1323 results: vec![
1324 TestResult::pass("test1"),
1325 TestResult::fail("test2", "error"),
1326 TestResult::pass("test3"),
1327 ],
1328 duration: Duration::ZERO,
1329 };
1330 assert_eq!(results.passed_count(), 2);
1331 assert_eq!(results.failed_count(), 1);
1332 assert_eq!(results.total(), 3);
1333 }
1334
1335 #[test]
1336 fn test_suite_results_failures() {
1337 let results = SuiteResults {
1338 suite_name: "test".to_string(),
1339 results: vec![
1340 TestResult::pass("test1"),
1341 TestResult::fail("test2", "error2"),
1342 TestResult::fail("test3", "error3"),
1343 ],
1344 duration: Duration::ZERO,
1345 };
1346 let failures = results.failures();
1347 assert_eq!(failures.len(), 2);
1348 assert_eq!(failures[0].name, "test2");
1349 assert_eq!(failures[1].name, "test3");
1350 }
1351
1352 #[test]
1353 fn test_harness_run_empty_suite() {
1354 let harness = TestHarness::new();
1355 let suite = TestSuite::new("Empty");
1356 let results = harness.run(&suite);
1357 assert!(results.all_passed());
1358 assert_eq!(results.total(), 0);
1359 }
1360
1361 #[test]
1362 fn test_harness_with_fail_fast() {
1363 let harness = TestHarness::new().with_fail_fast();
1364 assert!(harness.fail_fast);
1365 }
1366
1367 #[test]
1368 fn test_harness_with_parallel() {
1369 let harness = TestHarness::new().with_parallel();
1370 assert!(harness.parallel);
1371 }
1372
1373 #[test]
1374 fn test_harness_default() {
1375 let harness = TestHarness::default();
1376 assert!(!harness.fail_fast);
1377 assert!(!harness.parallel);
1378 }
1379 }
1380
1381 mod input_event_tests {
1382 use super::*;
1383
1384 #[test]
1385 fn test_input_event_touch() {
1386 let event = InputEvent::touch(100.0, 200.0);
1387 assert!(
1388 matches!(event, InputEvent::Touch { x, y } if (x - 100.0).abs() < f32::EPSILON && (y - 200.0).abs() < f32::EPSILON)
1389 );
1390 }
1391
1392 #[test]
1393 fn test_input_event_key_press() {
1394 let event = InputEvent::key_press("ArrowUp");
1395 assert!(matches!(event, InputEvent::KeyPress { key } if key == "ArrowUp"));
1396 }
1397
1398 #[test]
1399 fn test_input_event_key_release() {
1400 let event = InputEvent::key_release("Space");
1401 assert!(matches!(event, InputEvent::KeyRelease { key } if key == "Space"));
1402 }
1403
1404 #[test]
1405 fn test_input_event_mouse_click() {
1406 let event = InputEvent::mouse_click(50.0, 75.0);
1407 assert!(
1408 matches!(event, InputEvent::MouseClick { x, y } if (x - 50.0).abs() < f32::EPSILON && (y - 75.0).abs() < f32::EPSILON)
1409 );
1410 }
1411
1412 #[test]
1413 fn test_input_event_mouse_move() {
1414 let event = InputEvent::mouse_move(150.0, 250.0);
1415 assert!(
1416 matches!(event, InputEvent::MouseMove { x, y } if (x - 150.0).abs() < f32::EPSILON && (y - 250.0).abs() < f32::EPSILON)
1417 );
1418 }
1419
1420 #[test]
1421 fn test_input_event_gamepad_button_pressed() {
1422 let event = InputEvent::gamepad_button(0, true);
1423 assert!(matches!(
1424 event,
1425 InputEvent::GamepadButton {
1426 button: 0,
1427 pressed: true
1428 }
1429 ));
1430 }
1431
1432 #[test]
1433 fn test_input_event_gamepad_button_released() {
1434 let event = InputEvent::gamepad_button(1, false);
1435 assert!(matches!(
1436 event,
1437 InputEvent::GamepadButton {
1438 button: 1,
1439 pressed: false
1440 }
1441 ));
1442 }
1443
1444 #[test]
1445 fn test_touch_tap_coordinates() {
1446 let touch = Touch::tap(100.0, 200.0);
1447 assert!((touch.x - 100.0).abs() < f32::EPSILON);
1448 assert!((touch.y - 200.0).abs() < f32::EPSILON);
1449 assert!(matches!(touch.action, TouchAction::Tap));
1450 }
1451
1452 #[test]
1453 fn test_touch_swipe_full_properties() {
1454 let touch = Touch::swipe(10.0, 20.0, 100.0, 200.0, 300);
1455 assert!((touch.x - 10.0).abs() < f32::EPSILON);
1456 assert!((touch.y - 20.0).abs() < f32::EPSILON);
1457 match touch.action {
1458 TouchAction::Swipe {
1459 end_x,
1460 end_y,
1461 duration_ms,
1462 } => {
1463 assert!((end_x - 100.0).abs() < f32::EPSILON);
1464 assert!((end_y - 200.0).abs() < f32::EPSILON);
1465 assert_eq!(duration_ms, 300);
1466 }
1467 _ => panic!("expected Swipe action"),
1468 }
1469 }
1470
1471 #[test]
1472 fn test_touch_hold_full_properties() {
1473 let touch = Touch::hold(50.0, 60.0, 500);
1474 assert!((touch.x - 50.0).abs() < f32::EPSILON);
1475 assert!((touch.y - 60.0).abs() < f32::EPSILON);
1476 match touch.action {
1477 TouchAction::Hold { duration_ms } => {
1478 assert_eq!(duration_ms, 500);
1479 }
1480 _ => panic!("expected Hold action"),
1481 }
1482 }
1483
1484 #[test]
1485 fn test_touch_action_equality() {
1486 assert_eq!(TouchAction::Tap, TouchAction::Tap);
1487 let swipe1 = TouchAction::Swipe {
1488 end_x: 1.0,
1489 end_y: 2.0,
1490 duration_ms: 100,
1491 };
1492 let swipe2 = TouchAction::Swipe {
1493 end_x: 1.0,
1494 end_y: 2.0,
1495 duration_ms: 100,
1496 };
1497 assert_eq!(swipe1, swipe2);
1498 let hold1 = TouchAction::Hold { duration_ms: 500 };
1499 let hold2 = TouchAction::Hold { duration_ms: 500 };
1500 assert_eq!(hold1, hold2);
1501 }
1502
1503 #[test]
1504 fn test_touch_equality() {
1505 let t1 = Touch::tap(100.0, 200.0);
1506 let t2 = Touch::tap(100.0, 200.0);
1507 assert_eq!(t1, t2);
1508 }
1509
1510 #[test]
1511 fn test_input_event_equality() {
1512 let e1 = InputEvent::touch(10.0, 20.0);
1513 let e2 = InputEvent::touch(10.0, 20.0);
1514 assert_eq!(e1, e2);
1515 }
1516 }
1517
1518 mod error_tests {
1519 use super::*;
1520
1521 #[test]
1522 fn test_probar_error_display() {
1523 let err = ProbarError::BrowserNotFound;
1524 let msg = err.to_string();
1525 assert!(msg.contains("browser") || msg.contains("Browser"));
1526 }
1527
1528 #[test]
1529 fn test_probar_error_timeout() {
1530 let err = ProbarError::Timeout { ms: 5000 };
1531 let msg = err.to_string();
1532 assert!(msg.contains("5000"));
1533 }
1534 }
1535}