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